use crate::GeneratorContext;
use crate::types::TokenStreamWrapper;
use heck::{ToSnakeCase, ToUpperCamelCase};
use proc_macro2::{Ident, Span};
use quote::quote;
use std::fmt::{Display, Formatter};
use wit_bindgen_core::{TypeInfo, dealias};
use wit_parser::{Docs, Function, Handle, Type, TypeDefKind, TypeId};
pub fn escape_rust_ident(name: &str) -> String {
match name {
"as" => "as_".into(),
"break" => "break_".into(),
"const" => "const_".into(),
"continue" => "continue_".into(),
"crate" => "crate_".into(),
"else" => "else_".into(),
"enum" => "enum_".into(),
"extern" => "extern_".into(),
"false" => "false_".into(),
"fn" => "fn_".into(),
"for" => "for_".into(),
"if" => "if_".into(),
"impl" => "impl_".into(),
"in" => "in_".into(),
"let" => "let_".into(),
"loop" => "loop_".into(),
"match" => "match_".into(),
"mod" => "mod_".into(),
"move" => "move_".into(),
"mut" => "mut_".into(),
"pub" => "pub_".into(),
"ref" => "ref_".into(),
"return" => "return_".into(),
"self" => "self_".into(),
"static" => "static_".into(),
"struct" => "struct_".into(),
"super" => "super_".into(),
"trait" => "trait_".into(),
"true" => "true_".into(),
"type" => "type_".into(),
"unsafe" => "unsafe_".into(),
"use" => "use_".into(),
"where" => "where_".into(),
"while" => "while_".into(),
"async" => "async_".into(),
"await" => "await_".into(),
"dyn" => "dyn_".into(),
"abstract" => "abstract_".into(),
"become" => "become_".into(),
"box" => "box_".into(),
"do" => "do_".into(),
"final" => "final_".into(),
"macro" => "macro_".into(),
"override" => "override_".into(),
"priv" => "priv_".into(),
"typeof" => "typeof_".into(),
"unsized" => "unsized_".into(),
"virtual" => "virtual_".into(),
"yield" => "yield_".into(),
"try" => "try_".into(),
s => s.to_snake_case(),
}
}
#[derive(Debug, Copy, Clone)]
pub struct TypeMode {
pub lifetime: Option<&'static str>,
pub lists_borrowed: bool,
pub style: TypeOwnershipStyle,
pub type_info: Option<TypeInfo>,
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum TypeOwnershipStyle {
Owned,
OnlyTopBorrowed,
}
impl TypeMode {
fn owned(type_info: Option<TypeInfo>) -> TypeMode {
TypeMode {
lifetime: None,
lists_borrowed: false,
style: TypeOwnershipStyle::Owned,
type_info,
}
}
}
impl TypeOwnershipStyle {
pub fn next(&self) -> TypeOwnershipStyle {
match self {
TypeOwnershipStyle::Owned => TypeOwnershipStyle::Owned,
TypeOwnershipStyle::OnlyTopBorrowed => TypeOwnershipStyle::Owned,
}
}
}
pub fn type_mode_for_type_info(
info: TypeInfo,
style: TypeOwnershipStyle,
lt: &'static str,
) -> TypeMode {
let lifetime = if info.has_borrow_handle {
Some(lt)
} else if style == TypeOwnershipStyle::Owned {
None
} else if info.has_own_handle || !info.has_list {
None
} else if !info.owned {
Some(lt)
} else {
Some(lt)
};
TypeMode {
lifetime,
lists_borrowed: lifetime.is_some() && style != TypeOwnershipStyle::Owned,
style: if info.has_own_handle {
TypeOwnershipStyle::Owned
} else {
style
},
type_info: Some(info),
}
}
pub fn type_mode_for(
context: &GeneratorContext<'_>,
ty: &Type,
style: TypeOwnershipStyle,
lt: &'static str,
) -> TypeMode {
match ty {
Type::Id(id) => {
let info = context.bindgen_type_info(*id);
type_mode_for_type_info(info, style, lt)
}
Type::String if style != TypeOwnershipStyle::Owned => TypeMode {
lifetime: Some(lt),
lists_borrowed: true,
style,
type_info: None,
},
_ => TypeMode::owned(None),
}
}
pub fn filter_mode(context: &GeneratorContext<'_>, ty: &Type, mode: TypeMode) -> TypeMode {
match mode.lifetime {
Some(lt) => type_mode_for(context, ty, mode.style.next(), lt),
None => TypeMode::owned(mode.type_info),
}
}
pub fn filter_mode_preserve_top(
context: &GeneratorContext<'_>,
ty: &Type,
mode: TypeMode,
) -> TypeMode {
if mode.style == TypeOwnershipStyle::OnlyTopBorrowed {
mode
} else {
filter_mode(context, ty, mode)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RustType {
Bool,
U8,
U16,
U32,
U64,
S8,
S16,
S32,
S64,
F32,
F64,
Char,
ErrorContext,
Str {
lifetime: Option<String>,
},
String,
Vec {
element: Box<RustType>,
},
Slice {
lifetime: Option<String>,
element: Box<RustType>,
},
Owned {
name: String,
},
Borrowed {
name: String,
lifetime: Option<String>,
},
BorrowedResource {
name: String,
lifetime: Option<String>,
},
ResourceBorrow {
name: String,
lifetime: String,
},
Tuple {
items: Vec<RustType>,
},
Option {
inner: Box<RustType>,
},
Result {
ok: Box<RustType>,
err: Box<RustType>,
},
Unit,
}
impl RustType {
pub fn from_type(context: &GeneratorContext<'_>, ty: &Type, mode: TypeMode) -> Self {
if let Type::Id(id) = ty {
let id = dealias(&context.resolve, *id);
let typedef = &context.resolve.types[id];
match &typedef.kind {
TypeDefKind::Type(Type::String) => {
if let Some(lt) = mode.lifetime {
return Self::Str {
lifetime: if lt == "'_" {
None
} else {
Some(lt.to_string())
},
};
}
}
TypeDefKind::List(element) => {
if let Some(lt) = mode.lifetime {
let next_mode = filter_mode(context, ty, mode);
return if mode.lists_borrowed {
if lt == "'_" {
Self::Slice {
element: Box::new(RustType::from_type(
context, element, next_mode,
)),
lifetime: None,
}
} else {
Self::Slice {
element: Box::new(RustType::from_type(
context, element, next_mode,
)),
lifetime: Some(lt.to_string()),
}
}
} else {
Self::Vec {
element: Box::new(RustType::from_type(context, element, next_mode)),
}
};
}
}
_ => {}
}
}
match ty {
Type::Id(type_id) => Self::from_tyid(context, *type_id, mode),
Type::Bool => Self::Bool,
Type::U8 => Self::U8,
Type::U16 => Self::U16,
Type::U32 => Self::U32,
Type::U64 => Self::U64,
Type::S8 => Self::S8,
Type::S16 => Self::S16,
Type::S32 => Self::S32,
Type::S64 => Self::S64,
Type::F32 => Self::F32,
Type::F64 => Self::F64,
Type::Char => Self::Char,
Type::String => {
assert_eq!(mode.lists_borrowed, mode.lifetime.is_some());
match mode.lifetime {
Some("'_") => Self::Str { lifetime: None },
Some(lt) => Self::Str {
lifetime: Some(lt.to_string()),
},
None => Self::String,
}
}
Type::ErrorContext => Self::ErrorContext,
}
}
fn from_tyid(context: &GeneratorContext<'_>, id: TypeId, mode: TypeMode) -> Self {
let ty = &context.resolve.types[id];
if ty.name.is_some() && !Self::should_dealias(&ty.kind) {
let (_mode, lt) = if mode.style == TypeOwnershipStyle::OnlyTopBorrowed {
if let Some(lt) = mode.lifetime {
let info = context.bindgen_type_info(id);
(
type_mode_for_type_info(info, TypeOwnershipStyle::Owned, lt),
mode.lifetime,
)
} else {
(mode, None)
}
} else {
(mode, None)
};
let name = context.resolve.types[id]
.name
.as_ref()
.unwrap()
.to_upper_camel_case();
return match lt {
None => Self::Owned { name },
Some(lt) => Self::Borrowed {
name,
lifetime: if lt == "'_" {
None
} else {
Some(lt.to_string())
},
},
};
}
Self::from_anonymous_type(context, id, mode)
}
fn from_optional_type(
context: &GeneratorContext<'_>,
ty: Option<&Type>,
mode: TypeMode,
) -> Self {
match ty {
Some(ty) => {
let mode = filter_mode_preserve_top(context, ty, mode);
Self::from_type(context, ty, mode)
}
None => Self::Unit,
}
}
fn from_anonymous_type(context: &GeneratorContext<'_>, id: TypeId, mode: TypeMode) -> Self {
let ty = &context.resolve.types[id];
match &ty.kind {
TypeDefKind::Flags(_)
| TypeDefKind::Record(_)
| TypeDefKind::Resource
| TypeDefKind::Enum(_)
| TypeDefKind::Variant(_) => {
unreachable!()
}
TypeDefKind::Type(t) => Self::from_type(context, t, mode),
TypeDefKind::Tuple(tuple) => {
let mut items = Vec::new();
for ty in tuple.types.iter() {
let inner_mode = filter_mode_preserve_top(context, ty, mode);
let rust_typ = Self::from_type(context, ty, inner_mode);
items.push(rust_typ)
}
Self::Tuple { items }
}
TypeDefKind::Option(t) => {
let inner_mode = filter_mode_preserve_top(context, t, mode);
let rust_typ = Self::from_type(context, t, inner_mode);
Self::Option {
inner: Box::new(rust_typ),
}
}
TypeDefKind::Result(r) => {
let ok = Self::from_optional_type(context, r.ok.as_ref(), mode);
let err = Self::from_optional_type(context, r.err.as_ref(), mode);
Self::Result {
ok: Box::new(ok),
err: Box::new(err),
}
}
TypeDefKind::List(t) => {
let next_mode = filter_mode(context, t, mode);
if mode.lists_borrowed {
let lifetime = mode.lifetime.unwrap();
Self::Slice {
lifetime: if lifetime == "'_" {
None
} else {
Some(lifetime.to_string())
},
element: Box::new(RustType::from_type(context, t, next_mode)),
}
} else {
Self::Vec {
element: Box::new(RustType::from_type(context, t, next_mode)),
}
}
}
TypeDefKind::Future(_) => todo!(),
TypeDefKind::Stream(_) => todo!(),
TypeDefKind::Handle(handle) => match handle {
Handle::Own(ty) => Self::from_type(context, &Type::Id(*ty), mode),
Handle::Borrow(ty) => {
assert!(mode.lifetime.is_some());
let lt = mode.lifetime.unwrap();
if context.is_exported_type(*ty) {
let camel = context.resolve.types[*ty]
.name
.as_deref()
.unwrap()
.to_upper_camel_case();
let name = format!("{camel}Borrow");
Self::ResourceBorrow {
name,
lifetime: lt.to_string(),
}
} else {
let ty = &Type::Id(*ty);
let mode = filter_mode(context, ty, mode);
let RustType::Owned { name } = Self::from_type(context, ty, mode) else {
panic!("Unexpected result for borrowed imported handle type")
};
Self::BorrowedResource {
name,
lifetime: if lt == "'_" {
None
} else {
Some(lt.to_string())
},
}
}
}
},
TypeDefKind::FixedSizeList(..) => todo!(),
TypeDefKind::Unknown => unreachable!(),
}
}
pub fn adjust_export_side(self) -> RustType {
match self {
RustType::BorrowedResource { name, .. } => RustType::Owned { name },
RustType::Vec { element } => {
let element = element.adjust_export_side();
RustType::Vec {
element: Box::new(element),
}
}
RustType::Slice { element, lifetime } => {
let element = element.adjust_export_side();
RustType::Slice {
element: Box::new(element),
lifetime,
}
}
RustType::Tuple { items } => {
let items = items
.into_iter()
.map(|item| item.adjust_export_side())
.collect();
RustType::Tuple { items }
}
RustType::Option { inner } => {
let inner = inner.adjust_export_side();
RustType::Option {
inner: Box::new(inner),
}
}
RustType::Result { ok, err } => {
let ok = ok.adjust_export_side();
let err = err.adjust_export_side();
RustType::Result {
ok: Box::new(ok),
err: Box::new(err),
}
}
_ => self,
}
}
pub fn conversion_into_type(&self, other: &RustType) -> TokenStreamWrapper {
match (self, other) {
(
RustType::Owned { name: owned_name },
RustType::Borrowed {
name: borrowed_name,
..
},
) if owned_name == borrowed_name => TokenStreamWrapper::reference(),
(RustType::String, RustType::Str { .. }) => TokenStreamWrapper::as_str(),
(RustType::Str { .. }, RustType::String) => {
TokenStreamWrapper::new(|v| quote! { #v.to_string() })
}
(
RustType::Vec {
element: vec_element,
},
RustType::Slice {
element: slice_element,
..
},
) if vec_element == slice_element => TokenStreamWrapper::as_slice(),
(
RustType::Owned { name: owned_name },
RustType::BorrowedResource {
name: borrowed_name,
..
},
) if owned_name == borrowed_name => TokenStreamWrapper::reference(),
(
RustType::Owned { name: owned_name },
RustType::ResourceBorrow {
name: borrowed_name,
..
},
) if owned_name == borrowed_name => TokenStreamWrapper::reference(),
(RustType::Option { inner: from_inner }, RustType::Option { inner: to_inner }) => {
let cannot_map = to_inner.cannot_into_iter();
if cannot_map {
let adjusted_to_element = to_inner.in_iter();
let conversion = adjusted_to_element.conversion_into_type(to_inner);
match conversion {
TokenStreamWrapper::F(_) => {
let conversion = conversion.run(quote! { v });
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.map(|v| {
#conversion
})
}
})
}
TokenStreamWrapper::Ref => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_ref() }
}),
TokenStreamWrapper::Identity => TokenStreamWrapper::identity(),
TokenStreamWrapper::AsStr => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_deref() }
}),
TokenStreamWrapper::AsSlice => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_deref() }
}),
}
} else {
let conversion = from_inner.conversion_into_type(to_inner);
match conversion {
TokenStreamWrapper::F(_) => {
let conversion = conversion.run(quote! { v });
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.map(|v| {
#conversion
})
}
})
}
TokenStreamWrapper::Ref => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_ref() }
}),
TokenStreamWrapper::Identity => TokenStreamWrapper::identity(),
TokenStreamWrapper::AsStr => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_deref() }
}),
TokenStreamWrapper::AsSlice => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_deref() }
}),
}
}
}
(
RustType::Result {
ok: from_ok,
err: from_err,
},
RustType::Result {
ok: to_ok,
err: to_err,
},
) => {
let ok_conversion = from_ok.conversion_into_type(to_ok);
let err_conversion = from_err.conversion_into_type(to_err);
match (&ok_conversion, &err_conversion) {
(TokenStreamWrapper::F(_), TokenStreamWrapper::F(_)) => {
let ok_conversion = ok_conversion.run(quote! { v });
let err_conversion = err_conversion.run(quote! { v });
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.map(|v| {
#ok_conversion
}).map_err(|v| {
#err_conversion
})
}
})
}
(TokenStreamWrapper::Identity, TokenStreamWrapper::AsStr) => {
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.as_ref().map(|v| *v).map_err(|v| v.as_str())
}
})
}
(TokenStreamWrapper::Identity, TokenStreamWrapper::AsSlice) => {
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.as_ref().map(|v| *v).map_err(|v| v.as_slice())
}
})
}
(TokenStreamWrapper::AsStr, TokenStreamWrapper::Identity) => {
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.as_ref().map_err(|v| *v).map(|v| v.as_str())
}
})
}
(TokenStreamWrapper::AsSlice, TokenStreamWrapper::Identity) => {
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.as_ref().map_err(|v| *v).map(|v| v.as_slice())
}
})
}
_ => TokenStreamWrapper::identity(),
}
}
(
RustType::Vec {
element: from_element,
},
RustType::Slice {
element: to_element,
..
},
) => {
let cannot_into_iter = to_element.cannot_into_iter();
if cannot_into_iter {
let adjusted_to_element = to_element.in_iter();
let conversion = adjusted_to_element.conversion_into_type(to_element);
match conversion {
TokenStreamWrapper::F(_) => {
let conversion = conversion.run(quote! { v });
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.iter().map(|v| {
#conversion
}).collect::<Vec<_>>().as_slice()
}
})
}
TokenStreamWrapper::Ref => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_slice() }
}),
TokenStreamWrapper::Identity => TokenStreamWrapper::new(move |ts| {
quote! { #ts.as_slice() }
}),
TokenStreamWrapper::AsStr => TokenStreamWrapper::new(move |ts| {
quote! { #ts.iter().map(|v| v.as_str()).collect::<Vec<_>>().as_slice() }
}),
TokenStreamWrapper::AsSlice => TokenStreamWrapper::new(move |ts| {
quote! { #ts.iter().map(|v| v.as_slice()).collect::<Vec<_>>().as_slice() }
}),
}
} else {
let conversion = from_element.conversion_into_type(to_element);
match conversion {
TokenStreamWrapper::F(_) => {
let conversion = conversion.run(quote! { v });
TokenStreamWrapper::new(move |ts| {
quote! {
#ts.into_iter().map(|v| {
#conversion
}).collect()
}
})
}
TokenStreamWrapper::Ref => TokenStreamWrapper::new(move |ts| {
quote! { #ts.into_iter().map(|v| &v).collect() }
}),
TokenStreamWrapper::Identity => TokenStreamWrapper::identity(),
TokenStreamWrapper::AsStr => TokenStreamWrapper::new(move |ts| {
quote! { #ts.into_iter().map(|v| v.as_str()).collect() }
}),
TokenStreamWrapper::AsSlice => TokenStreamWrapper::new(move |ts| {
quote! { #ts.into_iter().map(|v| v.as_slice()).collect() }
}),
}
}
}
_ => {
TokenStreamWrapper::identity() }
}
}
pub fn cannot_into_iter(&self) -> bool {
self.contains(|t| matches!(t, RustType::BorrowedResource { .. }))
}
pub fn contains(&self, condition: impl Fn(&RustType) -> bool + Clone) -> bool {
if condition(self) {
true
} else {
match self {
RustType::Vec { element } => element.contains(condition),
RustType::Slice { element, .. } => element.contains(condition),
RustType::Tuple { items } => {
items.iter().any(|item| item.contains(condition.clone()))
}
RustType::Option { inner } => inner.contains(condition),
RustType::Result { ok, err } => {
ok.contains(condition.clone()) || err.contains(condition)
}
_ => false,
}
}
}
pub fn in_iter(&self) -> RustType {
match self {
RustType::Bool => self.clone(),
RustType::U8 => self.clone(),
RustType::U16 => self.clone(),
RustType::U32 => self.clone(),
RustType::U64 => self.clone(),
RustType::S8 => self.clone(),
RustType::S16 => self.clone(),
RustType::S32 => self.clone(),
RustType::S64 => self.clone(),
RustType::F32 => self.clone(),
RustType::F64 => self.clone(),
RustType::Char => self.clone(),
RustType::ErrorContext => self.clone(),
RustType::Str { .. } => self.clone(),
RustType::String => RustType::Str { lifetime: None },
RustType::Vec { element, .. } => RustType::Slice {
element: element.clone(),
lifetime: None,
},
RustType::Slice { .. } => self.clone(),
RustType::Owned { name } => RustType::Borrowed {
name: name.clone(),
lifetime: None,
},
RustType::Borrowed { .. } => self.clone(),
RustType::BorrowedResource { .. } => self.clone(),
RustType::ResourceBorrow { .. } => self.clone(),
RustType::Tuple { items } => RustType::Tuple {
items: items.iter().map(|item| item.in_iter()).collect(),
},
RustType::Option { .. } => self.clone(),
RustType::Result { .. } => self.clone(),
RustType::Unit => self.clone(),
}
}
fn should_dealias(kind: &TypeDefKind) -> bool {
!matches!(
kind,
TypeDefKind::Record(_)
| TypeDefKind::Variant(_)
| TypeDefKind::Enum(_)
| TypeDefKind::Flags(_)
| TypeDefKind::Handle(_)
| TypeDefKind::Resource
)
}
}
impl Display for RustType {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
RustType::Bool => write!(f, "bool"),
RustType::U8 => write!(f, "u8"),
RustType::U16 => write!(f, "u16"),
RustType::U32 => write!(f, "u32"),
RustType::U64 => write!(f, "u64"),
RustType::S8 => write!(f, "i8"),
RustType::S16 => write!(f, "i16"),
RustType::S32 => write!(f, "i32"),
RustType::S64 => write!(f, "i64"),
RustType::F32 => write!(f, "f32"),
RustType::F64 => write!(f, "f64"),
RustType::Char => write!(f, "char"),
RustType::ErrorContext => write!(f, "ErrorContext"),
RustType::Str { lifetime } => match lifetime {
Some(lt) => write!(f, "&{} str", lt),
None => write!(f, "&str"),
},
RustType::String => write!(f, "String"),
RustType::Vec { element } => write!(f, "Vec<{}>", element),
RustType::Slice { lifetime, element } => match lifetime {
Some(lt) => write!(f, "&{} [{}]", lt, element),
None => write!(f, "&[{}]", element),
},
RustType::Owned { name } => {
write!(f, "{}", name)
}
RustType::Borrowed { name, lifetime } => match lifetime {
Some(lt) => write!(f, "&{} {}", lt, name),
None => write!(f, "&{}", name),
},
RustType::BorrowedResource { name, lifetime } => match lifetime {
Some(lt) => write!(f, "&{} {}", lt, name),
None => write!(f, "&{}", name),
},
RustType::ResourceBorrow { name, lifetime } => {
write!(f, "&'{} {}", lifetime, name)
}
RustType::Tuple { items } => write!(
f,
"({})",
items
.iter()
.map(|item| item.to_string())
.collect::<Vec<_>>()
.join(", ")
),
RustType::Option { inner } => {
write!(f, "Option<{}>", inner)
}
RustType::Result { ok, err } => {
write!(f, "Result<{}, {}>", ok, err)
}
RustType::Unit => write!(f, "()"),
}
}
}
#[derive(Debug, Clone)]
pub struct RustFunctionParameter {
pub name: String,
pub typ: RustType,
}
impl RustFunctionParameter {
pub fn for_guest_import(context: &GeneratorContext<'_>, name: &str, ty: &Type) -> Self {
let name = escape_rust_ident(name);
let style = TypeOwnershipStyle::OnlyTopBorrowed;
let mode = type_mode_for(context, ty, style, "'_");
let typ = RustType::from_type(context, ty, mode);
Self { name, typ }
}
pub fn for_guest_export(context: &GeneratorContext<'_>, name: &str, ty: &Type) -> Self {
let name = escape_rust_ident(name);
let style = TypeOwnershipStyle::Owned;
let mode = type_mode_for(context, ty, style, "'_");
let typ = RustType::from_type(context, ty, mode).adjust_export_side();
Self { name, typ }
}
}
impl Display for RustFunctionParameter {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}: {}", self.name, self.typ)
}
}
#[allow(dead_code)]
pub struct RustWitFunction {
pub function_name: String,
pub import_parameters: Vec<RustFunctionParameter>,
pub export_parameters: Vec<RustFunctionParameter>,
pub return_type: RustType,
pub docs: Docs,
}
impl RustWitFunction {
pub fn new(context: &GeneratorContext<'_>, name: &str, function: &Function) -> Self {
Self {
function_name: escape_rust_ident(name),
import_parameters: function
.params
.iter()
.map(|(param_name, param_type)| {
RustFunctionParameter::for_guest_import(context, param_name, param_type)
})
.collect(),
export_parameters: function
.params
.iter()
.map(|(param_name, param_type)| {
RustFunctionParameter::for_guest_export(context, param_name, param_type)
})
.collect(),
return_type: if let Some(ty) = &function.result {
RustType::from_type(
context,
ty,
type_mode_for(context, ty, TypeOwnershipStyle::Owned, "'_"),
)
} else {
RustType::Unit
},
docs: function.docs.clone(),
}
}
pub fn function_name_ident(&self) -> Ident {
Ident::new(&self.function_name, Span::call_site())
}
}
impl Display for RustWitFunction {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}({})\n{}({})",
self.function_name,
self.import_parameters
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(", "),
self.function_name,
self.export_parameters
.iter()
.map(|p| p.to_string())
.collect::<Vec<_>>()
.join(", ")
)
}
}