use std::collections::HashMap;
use anyhow::{anyhow, Result};
use wit_parser::abi::WasmType;
use wit_parser::{
Docs, Resolve, Span, Stability, Tuple, Type, TypeDef, TypeDefKind, TypeId, TypeOwner,
};
use super::super::super::abi::emit::{wasm_type_to_val, BlobSlice};
use super::super::super::abi::flat_types;
use super::super::blob::NameInterner;
const ISSUES_URL: &str = "https://github.com/ejrgilbert/splicer/issues";
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) enum Cell {
Bool { flat_slot: u32 },
IntegerSignExt { flat_slot: u32 },
IntegerZeroExt { flat_slot: u32 },
Integer64 { flat_slot: u32 },
FloatingF32 { flat_slot: u32 },
FloatingF64 { flat_slot: u32 },
Text { ptr_slot: u32, len_slot: u32 },
Bytes { ptr_slot: u32, len_slot: u32 },
Char { flat_slot: u32 },
EnumCase {
flat_slot: u32,
type_name: BlobSlice,
case_names: Vec<BlobSlice>,
entry_offset: u32,
},
RecordOf {
type_name: BlobSlice,
fields: Vec<(BlobSlice, u32)>,
},
TupleOf { children: Vec<u32> },
Option { disc_slot: u32, child_idx: u32 },
Result {
disc_slot: u32,
ok_idx: Option<u32>,
err_idx: Option<u32>,
},
Flags {
flat_slot: u32,
type_name: BlobSlice,
flag_names: Vec<BlobSlice>,
},
Variant {
disc_slot: u32,
per_case_payload: Vec<Option<u32>>,
type_name: BlobSlice,
case_names: Vec<BlobSlice>,
},
Handle {
flat_slot: u32,
type_name: BlobSlice,
kind: HandleKind,
},
ListOf {
list_idx: u32,
ptr_slot: u32,
len_slot: u32,
element_plan: Box<LiftPlan>,
arm_guards: Vec<ArmGuard>,
},
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) struct ArmGuard {
pub(crate) disc_slot: u32,
pub(crate) expected_disc: u32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum HandleKind {
Resource,
Stream,
Future,
ErrorContext,
}
impl HandleKind {
pub(crate) fn cell_disc_case(self) -> &'static str {
match self {
HandleKind::Resource => "resource-handle",
HandleKind::Stream => "stream-handle",
HandleKind::Future => "future-handle",
HandleKind::ErrorContext => "error-context-handle",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum ListElementClass {
Scalar,
PrestagedChar,
PrestagedChildIdx,
PrestagedTupleIndices,
PrestagedHandle,
PrestagedFlags,
PrestagedRecord,
PrestagedVariant,
PrestagedNestedList,
}
impl Cell {
pub(crate) fn list_element_class(&self) -> ListElementClass {
match self {
Cell::Char { .. } => ListElementClass::PrestagedChar,
Cell::Option { .. } | Cell::Result { .. } => ListElementClass::PrestagedChildIdx,
Cell::TupleOf { .. } => ListElementClass::PrestagedTupleIndices,
Cell::Handle { .. } => ListElementClass::PrestagedHandle,
Cell::Flags { .. } => ListElementClass::PrestagedFlags,
Cell::RecordOf { .. } => ListElementClass::PrestagedRecord,
Cell::Variant { .. } => ListElementClass::PrestagedVariant,
Cell::Bool { .. }
| Cell::IntegerSignExt { .. }
| Cell::IntegerZeroExt { .. }
| Cell::Integer64 { .. }
| Cell::FloatingF32 { .. }
| Cell::FloatingF64 { .. }
| Cell::Text { .. }
| Cell::Bytes { .. }
| Cell::EnumCase { .. } => ListElementClass::Scalar,
Cell::ListOf { .. } => ListElementClass::PrestagedNestedList,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct LiftPlan {
pub(super) cells: Vec<Cell>,
pub flat_slot_count: u32,
slot_widening: Vec<Option<WasmType>>,
root: u32,
pub source_ty: Type,
}
impl LiftPlan {
pub(super) fn for_type(
ty: &Type,
resolve: &Resolve,
names: &mut NameInterner,
map_aliases: &MapAliases,
) -> Result<Self> {
let mut builder = LiftPlanBuilder::new(map_aliases);
let root = builder.push(ty, resolve, names);
if let Some(err) = builder.error {
return Err(err);
}
Ok(builder.into_plan(root, *ty))
}
pub(crate) fn cell_count(&self) -> u32 {
self.cells.len() as u32
}
pub(crate) fn root(&self) -> u32 {
self.root
}
pub(crate) fn widening_for(&self, flat_slot: u32) -> Option<WasmType> {
self.slot_widening
.get(flat_slot as usize)
.copied()
.flatten()
}
fn walk_cells_recursive(&self) -> Vec<&Cell> {
let mut out = Vec::with_capacity(self.cells.len());
for cell in &self.cells {
out.push(cell);
if let Cell::ListOf { element_plan, .. } = cell {
out.extend(element_plan.walk_cells_recursive());
}
}
out
}
pub(crate) fn contains_char(&self) -> bool {
self.walk_cells_recursive()
.iter()
.any(|c| matches!(c, Cell::Char { .. }))
}
pub(crate) fn has_list_elem_handle(&self) -> bool {
self.any_list_element_has_class(ListElementClass::PrestagedHandle)
}
pub(crate) fn has_list_elem_flags(&self) -> bool {
self.any_list_element_has_class(ListElementClass::PrestagedFlags)
}
pub(crate) fn has_list_elem_record(&self) -> bool {
self.any_list_element_has_class(ListElementClass::PrestagedRecord)
}
pub(crate) fn has_list_elem_variant(&self) -> bool {
self.any_list_element_has_class(ListElementClass::PrestagedVariant)
}
pub(crate) fn any_list_element_has_class(&self, want: ListElementClass) -> bool {
self.cells.iter().any(|c| {
let Cell::ListOf { element_plan, .. } = c else {
return false;
};
element_plan
.cells
.iter()
.any(|inner| inner.list_element_class() == want)
|| element_plan.any_list_element_has_class(want)
})
}
pub(super) fn stub_for(source_ty: Type) -> Self {
Self {
cells: vec![Cell::Bool { flat_slot: 0 }],
flat_slot_count: 1,
slot_widening: vec![None],
root: 0,
source_ty,
}
}
pub(crate) fn list_specs(&self) -> impl Iterator<Item = ListSpec<'_>> + '_ {
self.cells.iter().filter_map(|op| match op {
Cell::ListOf {
list_idx,
ptr_slot,
len_slot,
element_plan,
arm_guards,
} => Some(ListSpec {
list_idx: *list_idx,
ptr_slot: *ptr_slot,
len_slot: *len_slot,
element_plan,
arm_guards,
}),
_ => None,
})
}
}
#[derive(Clone, Copy)]
pub(crate) struct ListSpec<'a> {
pub list_idx: u32,
pub ptr_slot: u32,
pub len_slot: u32,
pub element_plan: &'a LiftPlan,
pub arm_guards: &'a [ArmGuard],
}
#[derive(Clone, Debug)]
pub(crate) struct MapAliases {
map_to_tuple: HashMap<TypeId, TypeId>,
}
impl MapAliases {
fn tuple_for(&self, map_id: TypeId) -> TypeId {
*self
.map_to_tuple
.get(&map_id)
.expect("desugar_map_aliases must run before classify for any Map typedef")
}
}
pub(crate) fn desugar_map_aliases(resolve: &mut Resolve) -> MapAliases {
let map_pairs: Vec<(TypeId, Type, Type)> = resolve
.types
.iter()
.filter_map(|(id, td)| match &td.kind {
TypeDefKind::Map(k, v) => Some((id, *k, *v)),
_ => None,
})
.collect();
let mut aliases = MapAliases {
map_to_tuple: HashMap::new(),
};
for (map_id, k, v) in map_pairs {
let tuple_id = resolve.types.alloc(TypeDef {
name: None,
kind: TypeDefKind::Tuple(Tuple { types: vec![k, v] }),
owner: TypeOwner::None,
docs: Docs::default(),
stability: Stability::default(),
span: Span::default(),
});
aliases.map_to_tuple.insert(map_id, tuple_id);
}
aliases
}
pub(super) struct LiftPlanBuilder<'a> {
cells: Vec<Cell>,
next_flat_slot: u32,
slot_widening: Vec<Option<WasmType>>,
next_list_idx: u32,
arm_guard_stack: Vec<ArmGuard>,
error: Option<anyhow::Error>,
map_aliases: &'a MapAliases,
}
impl<'a> LiftPlanBuilder<'a> {
pub(super) fn new(map_aliases: &'a MapAliases) -> Self {
Self {
cells: Vec::new(),
slot_widening: Vec::new(),
next_flat_slot: 0,
next_list_idx: 0,
arm_guard_stack: Vec::new(),
error: None,
map_aliases,
}
}
fn record_error(&mut self, err: anyhow::Error) {
if self.error.is_none() {
self.error = Some(err);
}
}
pub(super) fn push(&mut self, ty: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
match ty {
Type::Bool => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Bool { flat_slot })
}
Type::S8 | Type::S16 | Type::S32 => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::IntegerSignExt { flat_slot })
}
Type::U8 | Type::U16 | Type::U32 => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::IntegerZeroExt { flat_slot })
}
Type::S64 | Type::U64 => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Integer64 { flat_slot })
}
Type::F32 => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::FloatingF32 { flat_slot })
}
Type::F64 => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::FloatingF64 { flat_slot })
}
Type::String => {
let ptr_slot = self.bump_flat_slot();
let len_slot = self.bump_flat_slot();
self.push_cell(Cell::Text { ptr_slot, len_slot })
}
Type::Char => {
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Char { flat_slot })
}
Type::ErrorContext => {
let type_name = names.intern("");
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Handle {
flat_slot,
type_name,
kind: HandleKind::ErrorContext,
})
}
Type::Id(id) => match &resolve.types[*id].kind {
wit_parser::TypeDefKind::List(Type::U8) => {
let ptr_slot = self.bump_flat_slot();
let len_slot = self.bump_flat_slot();
self.push_cell(Cell::Bytes { ptr_slot, len_slot })
}
wit_parser::TypeDefKind::Enum(_) => {
let info = enum_lift_info_for_type(ty, resolve)
.expect("Enum kind implies enum-info available");
let type_name = names.intern(&info.type_name);
let case_names = info.item_names.iter().map(|n| names.intern(n)).collect();
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::EnumCase {
flat_slot,
type_name,
case_names,
entry_offset: 0,
})
}
wit_parser::TypeDefKind::Record(_) => self.push_record(ty, resolve, names),
wit_parser::TypeDefKind::Tuple(_) => self.push_tuple(ty, resolve, names),
wit_parser::TypeDefKind::Type(t) => self.push(t, resolve, names),
wit_parser::TypeDefKind::List(elem) => self.push_list_of(elem, resolve, names),
wit_parser::TypeDefKind::Variant(_) => self.push_variant(ty, resolve, names),
wit_parser::TypeDefKind::Flags(_) => {
let info = flags_lift_info_for_type(ty, resolve)
.expect("Flags kind implies flags-info available");
let type_name = names.intern(&info.type_name);
let flag_names = info.item_names.iter().map(|n| names.intern(n)).collect();
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Flags {
flat_slot,
type_name,
flag_names,
})
}
wit_parser::TypeDefKind::Option(inner) => self.push_option(inner, resolve, names),
wit_parser::TypeDefKind::Result(_) => self.push_result(ty, resolve, names),
wit_parser::TypeDefKind::Handle(h) => self.push_handle(h, resolve, names),
wit_parser::TypeDefKind::Stream(elem) => {
self.push_stream_or_future(elem.as_ref(), HandleKind::Stream, resolve, names)
}
wit_parser::TypeDefKind::Future(elem) => {
self.push_stream_or_future(elem.as_ref(), HandleKind::Future, resolve, names)
}
wit_parser::TypeDefKind::Resource => {
unreachable!(
"tier-2 lift: bare `Resource` at payload position is \
forbidden by canonical ABI"
)
}
wit_parser::TypeDefKind::Unknown => {
unreachable!("tier-2 lift: unresolved `Unknown` typedef")
}
wit_parser::TypeDefKind::Map(_, _) => {
let tuple_id = self.map_aliases.tuple_for(*id);
self.push_list_of(&Type::Id(tuple_id), resolve, names)
}
wit_parser::TypeDefKind::FixedLengthList(elem, n) => {
let elem = *elem;
let n = *n;
self.push_fixed_length_list(&elem, n, resolve, names)
}
},
}
}
fn bump_flat_slot(&mut self) -> u32 {
let r = self.next_flat_slot;
self.next_flat_slot = self
.next_flat_slot
.checked_add(1)
.expect("LiftPlanBuilder flat-slot counter overflowed u32");
if self.slot_widening.len() < self.next_flat_slot as usize {
self.slot_widening.push(None);
}
r
}
fn set_widening(&mut self, flat_slot: u32, joined_ty: WasmType) {
debug_assert!(
(flat_slot as usize) < self.slot_widening.len(),
"set_widening called for flat_slot {flat_slot} before bump_flat_slot reached it \
(slot_widening len = {})",
self.slot_widening.len(),
);
if let Some(prev) = self.slot_widening[flat_slot as usize] {
debug_assert_eq!(
wasm_type_to_val(prev),
wasm_type_to_val(joined_ty),
"set_widening overwriting slot {flat_slot} with a different joined type \
({prev:?} vs {joined_ty:?}) — joined should be structural"
);
}
self.slot_widening[flat_slot as usize] = Some(joined_ty);
}
fn push_cell(&mut self, cell: Cell) -> u32 {
let idx = self.cells.len() as u32;
self.cells.push(cell);
idx
}
fn push_record(&mut self, ty: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let Type::Id(id) = ty else {
unreachable!("Record kind came from non-Id type")
};
let typedef = &resolve.types[*id];
let wit_parser::TypeDefKind::Record(r) = &typedef.kind else {
unreachable!("Record kind came from non-Record TypeDefKind")
};
let type_name = names.intern(typedef.name.as_deref().unwrap_or(""));
let mut fields = Vec::with_capacity(r.fields.len());
for field in &r.fields {
let name_slice = names.intern(&field.name);
let child_idx = self.push(&field.ty, resolve, names);
fields.push((name_slice, child_idx));
}
self.push_cell(Cell::RecordOf { type_name, fields })
}
fn push_tuple(&mut self, ty: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let Type::Id(id) = ty else {
unreachable!("Tuple kind came from non-Id type")
};
let typedef = &resolve.types[*id];
let wit_parser::TypeDefKind::Tuple(t) = &typedef.kind else {
unreachable!("Tuple kind came from non-Tuple TypeDefKind")
};
let mut children = Vec::with_capacity(t.types.len());
for elem_ty in &t.types {
children.push(self.push(elem_ty, resolve, names));
}
debug_assert!(
!children.is_empty(),
"Cell::TupleOf must have ≥1 child — WIT forbids 0-tuples",
);
self.push_cell(Cell::TupleOf { children })
}
fn push_fixed_length_list(
&mut self,
elem: &Type,
n: u32,
resolve: &Resolve,
names: &mut NameInterner,
) -> u32 {
if n == 0 {
self.record_error(anyhow!(
"`list<T, N>` requires N >= 1; got N = 0. File a request at \
{ISSUES_URL} if a zero-length fixed list is intentional."
));
return self.stub_fixed_length_list();
}
if n > super::super::layout::MAX_FLAT_SLOTS_PER_FN
|| n > super::super::layout::MAX_CELLS_PER_PARAM
{
self.record_error(anyhow!(
"`list<T, N>` with N = {n} exceeds tier-2 layout budget \
(MAX_FLAT_SLOTS_PER_FN = {}, MAX_CELLS_PER_PARAM = {}). \
File a request at {ISSUES_URL} if this size is intentional.",
super::super::layout::MAX_FLAT_SLOTS_PER_FN,
super::super::layout::MAX_CELLS_PER_PARAM,
));
return self.stub_fixed_length_list();
}
let mut children = Vec::with_capacity(n as usize);
for _ in 0..n {
children.push(self.push(elem, resolve, names));
}
self.push_cell(Cell::TupleOf { children })
}
fn stub_fixed_length_list(&mut self) -> u32 {
let stub = self.push_cell(Cell::Bool { flat_slot: 0 });
self.push_cell(Cell::TupleOf {
children: vec![stub],
})
}
fn stub_disc_cell(&mut self) -> u32 {
self.push_cell(Cell::Bool { flat_slot: 0 })
}
fn record_flat_overflow(&mut self, kind: &str) {
self.record_error(anyhow!(
"{kind} flat representation exceeds MAX_FLAT_PARAMS ({})",
Resolve::MAX_FLAT_PARAMS,
));
}
fn push_option(&mut self, inner: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let disc_slot = self.bump_flat_slot();
let child_idx = self.push(inner, resolve, names);
self.push_cell(Cell::Option {
disc_slot,
child_idx,
})
}
fn push_result(&mut self, ty: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let Type::Id(id) = ty else {
unreachable!("Result kind came from non-Id type")
};
let wit_parser::TypeDefKind::Result(r) = &resolve.types[*id].kind else {
unreachable!("Result kind came from non-Result TypeDefKind")
};
let r = r.clone();
let Some(joined) = flat_types(resolve, ty, None) else {
self.record_flat_overflow("result<T, E>");
return self.stub_disc_cell();
};
let disc_slot = self.bump_flat_slot();
let arms_base = self.next_flat_slot;
let [ok_idx, err_idx]: [Option<u32>; 2] = self
.push_disc_arms(disc_slot, arms_base, &joined, [r.ok, r.err], resolve, names)
.try_into()
.expect("push_disc_arms with 2-element input returns 2-element output");
self.push_cell(Cell::Result {
disc_slot,
ok_idx,
err_idx,
})
}
fn push_arm<R>(
&mut self,
disc_slot: u32,
expected_disc: u32,
walk: impl FnOnce(&mut Self) -> R,
) -> R {
self.arm_guard_stack.push(ArmGuard {
disc_slot,
expected_disc,
});
let r = walk(self);
self.arm_guard_stack.pop();
r
}
fn record_arm_widening(
&mut self,
arm: Option<&Type>,
arms_base: u32,
joined: &[WasmType],
resolve: &Resolve,
) {
let Some(t) = arm else { return };
let arm_flat =
flat_types(resolve, t, None).expect("arm flat fits — joined fit, so arm fits");
for (i, &arm_ty) in arm_flat.iter().enumerate() {
let joined_ty = joined[1 + i];
if wasm_type_to_val(arm_ty) != wasm_type_to_val(joined_ty) {
self.set_widening(arms_base + i as u32, joined_ty);
}
}
}
fn push_variant(&mut self, ty: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let Type::Id(id) = ty else {
unreachable!("Variant kind came from non-Id type")
};
let typedef = &resolve.types[*id];
let wit_parser::TypeDefKind::Variant(v) = &typedef.kind else {
unreachable!("Variant kind came from non-Variant TypeDefKind")
};
let v = v.clone();
let info = variant_lift_info_for_type(ty, resolve)
.expect("Variant kind implies variant-info available");
let type_name = names.intern(&info.type_name);
let case_names = info.item_names.iter().map(|n| names.intern(n)).collect();
let Some(joined) = flat_types(resolve, ty, None) else {
self.record_flat_overflow("variant");
return self.stub_disc_cell();
};
let disc_slot = self.bump_flat_slot();
let arms_base = self.next_flat_slot;
let per_case_payload = self.push_disc_arms(
disc_slot,
arms_base,
&joined,
v.cases.iter().map(|c| c.ty),
resolve,
names,
);
self.push_cell(Cell::Variant {
disc_slot,
per_case_payload,
type_name,
case_names,
})
}
fn push_disc_arms<I>(
&mut self,
disc_slot: u32,
arms_base: u32,
joined: &[WasmType],
arms: I,
resolve: &Resolve,
names: &mut NameInterner,
) -> Vec<Option<u32>>
where
I: IntoIterator<Item = Option<Type>>,
{
let mut max_after = arms_base;
let mut indices: Vec<Option<u32>> = Vec::new();
for (disc, arm) in arms.into_iter().enumerate() {
self.next_flat_slot = arms_base;
let child_idx = self.push_arm(disc_slot, disc as u32, |b| {
arm.map(|t| b.push(&t, resolve, names))
});
max_after = max_after.max(self.next_flat_slot);
self.record_arm_widening(arm.as_ref(), arms_base, joined, resolve);
indices.push(child_idx);
}
self.next_flat_slot = max_after;
indices
}
fn push_handle(
&mut self,
h: &wit_parser::Handle,
resolve: &Resolve,
names: &mut NameInterner,
) -> u32 {
let resource_id = match h {
wit_parser::Handle::Own(id) | wit_parser::Handle::Borrow(id) => *id,
};
let type_name = names.intern(resolve.types[resource_id].name.as_deref().unwrap_or(""));
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Handle {
flat_slot,
type_name,
kind: HandleKind::Resource,
})
}
fn push_stream_or_future(
&mut self,
elem: Option<&Type>,
kind: HandleKind,
resolve: &Resolve,
names: &mut NameInterner,
) -> u32 {
let elem_name = elem
.and_then(|t| match t {
Type::Id(id) => Some(*id),
_ => None,
})
.map(|id| {
let mut tid = id;
loop {
let td = &resolve.types[tid];
if let Some(name) = td.name.as_deref() {
return name;
}
match &td.kind {
wit_parser::TypeDefKind::Type(Type::Id(next)) => tid = *next,
wit_parser::TypeDefKind::Handle(
wit_parser::Handle::Own(next) | wit_parser::Handle::Borrow(next),
) => tid = *next,
_ => return "",
}
}
})
.unwrap_or("");
let type_name = names.intern(elem_name);
let flat_slot = self.bump_flat_slot();
self.push_cell(Cell::Handle {
flat_slot,
type_name,
kind,
})
}
fn push_list_of(&mut self, elem: &Type, resolve: &Resolve, names: &mut NameInterner) -> u32 {
let list_idx = self.next_list_idx;
self.next_list_idx += 1;
let ptr_slot = self.bump_flat_slot();
let len_slot = self.bump_flat_slot();
if flat_types(resolve, elem, None).is_none() {
self.record_flat_overflow("list element");
}
let element_plan = match LiftPlan::for_type(elem, resolve, names, self.map_aliases) {
Ok(plan) => plan,
Err(err) => {
self.record_error(err);
LiftPlan::stub_for(*elem)
}
};
let arm_guards = self.arm_guard_stack.clone();
self.push_cell(Cell::ListOf {
list_idx,
ptr_slot,
len_slot,
element_plan: Box::new(element_plan),
arm_guards,
})
}
pub(super) fn into_plan(self, root: u32, source_ty: Type) -> LiftPlan {
debug_assert_eq!(
self.slot_widening.len() as u32,
self.next_flat_slot,
"slot_widening must mirror flat_slot_count (one entry per bump_flat_slot)",
);
LiftPlan {
cells: self.cells,
flat_slot_count: self.next_flat_slot,
slot_widening: self.slot_widening,
root,
source_ty,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct NamedListInfo {
pub(super) type_name: String,
pub(super) item_names: Vec<String>,
}
fn lift_info_for_type<F>(ty: &Type, resolve: &Resolve, kind_extract: F) -> Option<NamedListInfo>
where
F: FnOnce(&wit_parser::TypeDefKind) -> Option<Vec<String>>,
{
let Type::Id(id) = ty else {
return None;
};
let typedef = &resolve.types[*id];
let item_names = kind_extract(&typedef.kind)?;
let type_name = typedef.name.as_ref()?.clone();
Some(NamedListInfo {
type_name,
item_names,
})
}
fn enum_lift_info_for_type(ty: &Type, resolve: &Resolve) -> Option<NamedListInfo> {
lift_info_for_type(ty, resolve, |k| match k {
wit_parser::TypeDefKind::Enum(e) => Some(e.cases.iter().map(|c| c.name.clone()).collect()),
_ => None,
})
}
fn variant_lift_info_for_type(ty: &Type, resolve: &Resolve) -> Option<NamedListInfo> {
lift_info_for_type(ty, resolve, |k| match k {
wit_parser::TypeDefKind::Variant(v) => {
Some(v.cases.iter().map(|c| c.name.clone()).collect())
}
_ => None,
})
}
fn flags_lift_info_for_type(ty: &Type, resolve: &Resolve) -> Option<NamedListInfo> {
lift_info_for_type(ty, resolve, |k| match k {
wit_parser::TypeDefKind::Flags(fl) => {
Some(fl.flags.iter().map(|f| f.name.clone()).collect())
}
_ => None,
})
}