1use std::fmt::Write;
2
3use anyhow::{bail, Result};
4use syn::{
5 Attribute, Field, Fields, ItemEnum, ItemFn, ItemImpl, ItemStruct, ItemTrait, ItemType, ItemUse,
6 Lit, Signature, TraitItem, UseGroup, UseName, UsePath, UseRename, UseTree,
7};
8
9use crate::{
10 util::{pub_method, wit_ident, FuncType, SignatureUtils},
11 wit::ToWitType,
12};
13
14pub fn gen_wit_struct(strukt: &ItemStruct) -> Result<String> {
33 if !strukt.generics.params.is_empty() {
34 bail!("doesn't support generic parameters with witgen");
35 }
36
37 let struct_name = wit_ident(&strukt.ident)?;
38 let is_tuple_struct = strukt.fields.iter().any(|f| f.ident.is_none());
39 let fields = gen_fields(strukt.fields.iter().collect::<Vec<&Field>>())?;
40 let fields = if is_tuple_struct {
41 fields.join(", ")
42 } else {
43 fields.join(",\n")
44 };
45
46 let content = if is_tuple_struct {
47 format!("type {} = tuple<{}>\n", struct_name, fields)
48 } else {
49 format!(
50 r#"record {} {{
51{}
52}}
53"#,
54 struct_name, fields
55 )
56 };
57 Ok(content)
58}
59
60fn gen_fields(iter: Vec<&Field>) -> Result<Vec<String>> {
61 iter.into_iter()
62 .map(|field| {
63 let field_name = if let Some(ident) = &field.ident {
64 format!(" {}: ", wit_ident(ident)?)
65 } else {
66 Default::default()
67 };
68 let comment = get_doc_comment(&field.attrs, 1, false)?;
69 Ok(format!("{comment}{}{}", field_name, field.ty.to_wit()?))
70 })
71 .collect()
72}
73
74pub fn gen_wit_enum(enm: &ItemEnum) -> Result<String> {
93 if !enm.generics.params.is_empty() {
94 bail!("doesn't support generic parameters with witgen");
95 }
96
97 let enm_name = wit_ident(&enm.ident)?;
98 let is_wit_enum = enm
99 .variants
100 .iter()
101 .all(|v| matches!(v.fields, Fields::Unit));
102 let mut named_types = String::new();
103 let variants = enm
104 .variants
105 .iter()
106 .map(|variant| {
107 let ident = wit_ident(&variant.ident)?;
108 let comment = get_doc_comment(&variant.attrs, 1, false)?;
109 let variant_string = match &variant.fields {
110 syn::Fields::Named(named) => {
111 let fields = gen_fields(named.named.iter().collect())?.join(",\n");
112 let inner_type_name = &format!("{}-{}", enm_name, ident);
113 let comment = get_doc_comment(&variant.attrs, 0, false)?;
114 write!(
115 &mut named_types,
116 "{}record {} {{\n{}\n}}\n",
117 comment, inner_type_name, fields
118 )?;
119 Ok(format!("{}({})", ident, inner_type_name))
120 }
121 syn::Fields::Unnamed(unamed) => {
122 let fields = unamed
123 .unnamed
124 .iter()
125 .map(|field| field.ty.to_wit())
126 .collect::<Result<Vec<String>>>()?
127 .join(", ");
128
129 let formatted_field = if unamed.unnamed.len() > 1 {
130 format!("tuple<{}>", fields)
131 } else {
132 fields
133 };
134
135 Ok(format!("{}({})", ident, formatted_field))
136 }
137 syn::Fields::Unit => Ok(ident),
138 };
139 variant_string.map(|v| format!("{} {},", comment, v))
140 })
141 .collect::<Result<Vec<String>>>()?
142 .join("\n");
143 let ty = if is_wit_enum { "enum" } else { "variant" };
144 let content = format!(
145 r#"{} {} {{
146{}
147}}
148"#,
149 ty, enm_name, variants
150 );
151
152 Ok(format!("{}{}", content, named_types))
153}
154
155pub fn gen_wit_function(func: &ItemFn) -> Result<String> {
167 let signature = &func.sig;
168 gen_wit_function_from_signature(signature)
169}
170
171fn gen_wit_function_from_signature(signature: &Signature) -> Result<String> {
172 let fn_name = wit_ident(&signature.ident)?;
173 let fn_type = signature.fn_type();
174 let fn_args = signature.fn_args()?.join(", ");
175 let ret = signature.ret_args()?;
176
177 let preamble = if let FuncType::Instance(true) = fn_type {
178 "///@mutable\n "
179 } else {
180 ""
181 }
182 .to_string();
183
184 Ok(format!("{preamble}{fn_name}: func({fn_args}){ret}\n"))
185}
186
187pub fn gen_wit_type_alias(type_alias: &ItemType) -> Result<String> {
199 if !type_alias.generics.params.is_empty() {
200 bail!("doesn't support generic parameters with witgen");
201 }
202 let ty = type_alias.ty.to_wit()?;
203 let type_alias_ident = wit_ident(&type_alias.ident)?;
204 Ok(format!("type {} = {}\n", type_alias_ident, ty))
205}
206
207pub(crate) fn get_doc_comment(
208 attrs: &[Attribute],
209 depth: usize,
210 include_paths: bool,
211) -> Result<String> {
212 let mut comment = String::new();
213 let spaces = " ".repeat(depth * 2);
214 for attr in attrs {
215 match &attr.parse_meta()? {
216 syn::Meta::NameValue(name_val) if name_val.path.is_ident("doc") => {
217 if let Lit::Str(lit_str) = &name_val.lit {
218 let text = lit_str.value();
219 writeln!(&mut comment, "{spaces}///{text}",)?;
220 }
221 }
222 syn::Meta::Path(path) if include_paths => {
223 if let Some(ident) = path.get_ident() {
224 writeln!(&mut comment, "{spaces}///@{ident}")?;
225 }
226 }
227 _ => {}
228 }
229 }
230 Ok(comment)
231}
232
233#[allow(unused_variables, unreachable_code)]
234fn gen_use_names(use_tree: &UseTree) -> Result<String> {
235 let res = match use_tree {
236 UseTree::Path(_) => {
237 todo!("Can only have top level path, e.g. cannot do `use other_crate::module::...`")
238 }
239 UseTree::Name(UseName { ident }) => {
240 todo!("Cannot specify one import must be *. e.g. `use other_crate::Import` --> `use other_crate::*`");
241 wit_ident(&ident)?
242 },
243 UseTree::Rename(UseRename { ident, rename, .. }) => {
244 todo!("Cannot have renamed imports yet. E.g. `use other_crate::Import as OtherImport`");
246 let (ident, rename) = (wit_ident(ident)?, (wit_ident(rename)?));
247 format!("{ident} as {rename}")
248 }
249 UseTree::Glob(_) => return Ok("*".to_string()),
250 UseTree::Group(UseGroup { items, .. }) => {
251 todo!("Cannot specify group yet. E.g. `use other_crate::{{Import1, Import2}}`");
252 items
253 .iter()
254 .map(gen_use_names)
255 .collect::<Result<Vec<String>>>()?
256 .join(",")},
257 };
258 Ok(format!("{{{res}}}"))
259}
260
261pub fn gen_wit_import(import: &ItemUse) -> Result<String> {
262 let (id, use_names) = match &import.tree {
263 UseTree::Path(UsePath { ident, tree, .. }) => {
264 (wit_ident(ident)?, gen_use_names(tree)?)
265 }
266 ,
267 _ => todo!("Can only use top level 'path', e.g. `use import_crate::*` -> `import_crate`. More specific imports is a TODO."),
268 };
269 let res = format!("use {use_names} from {id}");
271 println!("{res}");
272 Ok(res)
273}
274
275pub fn gen_wit_trait(trait_: &ItemTrait) -> Result<String> {
276 let name = wit_ident(&trait_.ident)?;
277 let mut res = format!("interface {name} {{\n");
278
279 for item in trait_.items.iter() {
280 match item {
281 TraitItem::Const(_) => todo!("Const in Trait isn't implemented yet"),
282 TraitItem::Method(method) => {
283 let comment = get_doc_comment(&method.attrs, 1, true)?;
284 write!(
285 &mut res,
286 "{comment} {}",
287 gen_wit_function_from_signature(&method.sig)?
288 )?
289 }
290 TraitItem::Type(_) => todo!("Type in Trait isn't implemented yet"),
291 TraitItem::Macro(_) => todo!("Macro in Trait isn't implemented yet"),
292 TraitItem::Verbatim(_) => todo!("Verbatim in Trait isn't implemented yet"),
293 _ => todo!("extra case in Trait isn't implemented yet"),
294 }
295 }
296 res.push_str("}\n");
297 Ok(res)
298}
299
300pub fn gen_wit_impl(impl_: &ItemImpl) -> Result<String> {
301 let name = wit_ident(&impl_.self_ty.to_wit()?)?;
302 let mut res = format!("resource {name} {{\n");
303 for method in impl_.items.iter().filter_map(pub_method) {
304 let comment = get_doc_comment(&method.attrs, 1, true)?;
305 let static_decl = if matches!(method.sig.fn_type(), FuncType::Standalone) {
306 "static "
307 } else {
308 ""
309 }
310 .to_string();
311 write!(
312 &mut res,
313 "{comment} {static_decl}{}",
314 gen_wit_function_from_signature(&method.sig)?
315 )?
316 }
317 res.push_str("}\n");
318 Ok(res)
319}