1 use crate::{Ownership, types::TypeInfo};
2 use heck::*;
3 use wit_parser::*;
4
5 #[derive(Debug, Copy, Clone, PartialEq)]
6 pub enum TypeMode {
7 Owned,
8 AllBorrowed(&'static str),
9 }
10
11 pub trait RustGenerator<'a> {
resolve(&self) -> &'a Resolve12 fn resolve(&self) -> &'a Resolve;
13
push_str(&mut self, s: &str)14 fn push_str(&mut self, s: &str);
info(&self, ty: TypeId) -> TypeInfo15 fn info(&self, ty: TypeId) -> TypeInfo;
path_to_interface(&self, interface: InterfaceId) -> Option<String>16 fn path_to_interface(&self, interface: InterfaceId) -> Option<String>;
is_imported_interface(&self, interface: InterfaceId) -> bool17 fn is_imported_interface(&self, interface: InterfaceId) -> bool;
wasmtime_path(&self) -> String18 fn wasmtime_path(&self) -> String;
19
20 /// This determines whether we generate owning types or (where appropriate)
21 /// borrowing types.
22 ///
23 /// For example, when generating a type which is only used as a parameter to
24 /// a guest-exported function, there is no need for it to own its fields.
25 /// However, constructing deeply-nested borrows (e.g. `&[&[&[&str]]]]` for
26 /// `list<list<list<string>>>`) can be very awkward, so by default we
27 /// generate owning types and use only shallow borrowing at the top level
28 /// inside function signatures.
ownership(&self) -> Ownership29 fn ownership(&self) -> Ownership;
30
print_ty(&mut self, ty: &Type, mode: TypeMode)31 fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
32 self.push_str(&self.ty(ty, mode))
33 }
ty(&self, ty: &Type, mode: TypeMode) -> String34 fn ty(&self, ty: &Type, mode: TypeMode) -> String {
35 match ty {
36 Type::Id(t) => self.tyid(*t, mode),
37 Type::Bool => "bool".to_string(),
38 Type::U8 => "u8".to_string(),
39 Type::U16 => "u16".to_string(),
40 Type::U32 => "u32".to_string(),
41 Type::U64 => "u64".to_string(),
42 Type::S8 => "i8".to_string(),
43 Type::S16 => "i16".to_string(),
44 Type::S32 => "i32".to_string(),
45 Type::S64 => "i64".to_string(),
46 Type::F32 => "f32".to_string(),
47 Type::F64 => "f64".to_string(),
48 Type::Char => "char".to_string(),
49 Type::String => match mode {
50 TypeMode::AllBorrowed(lt) => {
51 if lt != "'_" {
52 format!("&{lt} str")
53 } else {
54 format!("&str")
55 }
56 }
57 TypeMode::Owned => {
58 let wt = self.wasmtime_path();
59 format!("{wt}::component::__internal::String")
60 }
61 },
62 Type::ErrorContext => {
63 let wt = self.wasmtime_path();
64 format!("{wt}::component::ErrorContext")
65 }
66 }
67 }
68
print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode)69 fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
70 self.push_str(&self.optional_ty(ty, mode))
71 }
optional_ty(&self, ty: Option<&Type>, mode: TypeMode) -> String72 fn optional_ty(&self, ty: Option<&Type>, mode: TypeMode) -> String {
73 match ty {
74 Some(ty) => self.ty(ty, mode),
75 None => "()".to_string(),
76 }
77 }
78
tyid(&self, id: TypeId, mode: TypeMode) -> String79 fn tyid(&self, id: TypeId, mode: TypeMode) -> String {
80 let info = self.info(id);
81 let lt = self.lifetime_for(&info, mode);
82 let ty = &self.resolve().types[id];
83 if ty.name.is_some() {
84 // If this type has a list internally, no lifetime is being printed,
85 // but we're in a borrowed mode, then that means we're in a borrowed
86 // context and don't want ownership of the type but we're using an
87 // owned type definition. Inject a `&` in front to indicate that, at
88 // the API level, ownership isn't required.
89 let mut out = String::new();
90 if info.has_list && lt.is_none() {
91 if let TypeMode::AllBorrowed(lt) = mode {
92 if lt != "'_" {
93 out.push_str(&format!("&{lt} "))
94 } else {
95 out.push_str("&")
96 }
97 }
98 }
99 let name = if lt.is_some() {
100 self.param_name(id)
101 } else {
102 self.result_name(id)
103 };
104 out.push_str(&self.type_name_in_interface(ty.owner, &name));
105
106 // If the type recursively owns data and it's a
107 // variant/record/list, then we need to place the
108 // lifetime parameter on the type as well.
109 if info.has_list && needs_generics(self.resolve(), &ty.kind) {
110 out.push_str(&self.generics(lt));
111 }
112
113 return out;
114
115 fn needs_generics(resolve: &Resolve, ty: &TypeDefKind) -> bool {
116 match ty {
117 TypeDefKind::Variant(_)
118 | TypeDefKind::Record(_)
119 | TypeDefKind::Option(_)
120 | TypeDefKind::Result(_)
121 | TypeDefKind::Future(_)
122 | TypeDefKind::Stream(_)
123 | TypeDefKind::List(_)
124 | TypeDefKind::Map(_, _)
125 | TypeDefKind::Flags(_)
126 | TypeDefKind::Enum(_)
127 | TypeDefKind::Tuple(_)
128 | TypeDefKind::Handle(_)
129 | TypeDefKind::Resource => true,
130 TypeDefKind::Type(Type::Id(t)) => {
131 needs_generics(resolve, &resolve.types[*t].kind)
132 }
133 TypeDefKind::Type(Type::String) => true,
134 TypeDefKind::Type(_) => false,
135 TypeDefKind::Unknown => unreachable!(),
136 TypeDefKind::FixedLengthList(..) => todo!(),
137 }
138 }
139 }
140
141 match &ty.kind {
142 TypeDefKind::List(t) => self.list(t, mode),
143
144 TypeDefKind::Option(t) => {
145 format!("Option<{}>", self.ty(t, mode))
146 }
147
148 TypeDefKind::Result(r) => {
149 let ok = self.optional_ty(r.ok.as_ref(), mode);
150 let err = self.optional_ty(r.err.as_ref(), mode);
151 format!("Result<{ok},{err}>")
152 }
153
154 TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
155
156 // Tuple-like records are mapped directly to Rust tuples of
157 // types. Note the trailing comma after each member to
158 // appropriately handle 1-tuples.
159 TypeDefKind::Tuple(t) => {
160 let mut out = "(".to_string();
161 for ty in t.types.iter() {
162 out.push_str(&self.ty(ty, mode));
163 out.push_str(",");
164 }
165 out.push_str(")");
166 out
167 }
168 TypeDefKind::Record(_) => {
169 panic!("unsupported anonymous type reference: record")
170 }
171 TypeDefKind::Flags(_) => {
172 panic!("unsupported anonymous type reference: flags")
173 }
174 TypeDefKind::Enum(_) => {
175 panic!("unsupported anonymous type reference: enum")
176 }
177 TypeDefKind::Future(ty) => {
178 let wt = self.wasmtime_path();
179 let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
180 format!("{wt}::component::FutureReader<{t}>")
181 }
182 TypeDefKind::Stream(ty) => {
183 let wt = self.wasmtime_path();
184 let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
185 format!("{wt}::component::StreamReader<{t}>")
186 }
187 TypeDefKind::Handle(handle) => self.handle(handle),
188 TypeDefKind::Resource => unreachable!(),
189
190 TypeDefKind::Type(t) => self.ty(t, mode),
191 TypeDefKind::Map(k, v) => {
192 let key = self.ty(k, mode);
193 let value = self.ty(v, mode);
194 format!("std::collections::HashMap<{key}, {value}>")
195 }
196 TypeDefKind::Unknown => unreachable!(),
197 TypeDefKind::FixedLengthList(..) => todo!(),
198 }
199 }
200
type_name_in_interface(&self, owner: TypeOwner, name: &str) -> String201 fn type_name_in_interface(&self, owner: TypeOwner, name: &str) -> String {
202 let mut out = String::new();
203 if let TypeOwner::Interface(id) = owner {
204 if let Some(path) = self.path_to_interface(id) {
205 out.push_str(&path);
206 out.push_str("::");
207 }
208 }
209 out.push_str(name);
210 out
211 }
212
print_list(&mut self, ty: &Type, mode: TypeMode)213 fn print_list(&mut self, ty: &Type, mode: TypeMode) {
214 self.push_str(&self.list(ty, mode))
215 }
list(&self, ty: &Type, mode: TypeMode) -> String216 fn list(&self, ty: &Type, mode: TypeMode) -> String {
217 let next_mode = if matches!(self.ownership(), Ownership::Owning) {
218 TypeMode::Owned
219 } else {
220 mode
221 };
222 let ty = self.ty(ty, next_mode);
223 match mode {
224 TypeMode::AllBorrowed(lt) => {
225 if lt != "'_" {
226 format!("&{lt} [{ty}]")
227 } else {
228 format!("&[{ty}]")
229 }
230 }
231 TypeMode::Owned => {
232 let wt = self.wasmtime_path();
233 format!("{wt}::component::__internal::Vec<{ty}>")
234 }
235 }
236 }
237
print_stream(&mut self, ty: Option<&Type>)238 fn print_stream(&mut self, ty: Option<&Type>) {
239 self.push_str(&self.stream(ty))
240 }
stream(&self, ty: Option<&Type>) -> String241 fn stream(&self, ty: Option<&Type>) -> String {
242 let wt = self.wasmtime_path();
243 let mut out = format!("{wt}::component::HostStream<");
244 out.push_str(&self.optional_ty(ty, TypeMode::Owned));
245 out.push_str(">");
246 out
247 }
248
print_future(&mut self, ty: Option<&Type>)249 fn print_future(&mut self, ty: Option<&Type>) {
250 self.push_str(&self.future(ty))
251 }
future(&self, ty: Option<&Type>) -> String252 fn future(&self, ty: Option<&Type>) -> String {
253 let wt = self.wasmtime_path();
254 let mut out = format!("{wt}::component::HostFuture<");
255 out.push_str(&self.optional_ty(ty, TypeMode::Owned));
256 out.push_str(">");
257 out
258 }
259
print_handle(&mut self, handle: &Handle)260 fn print_handle(&mut self, handle: &Handle) {
261 self.push_str(&self.handle(handle))
262 }
handle(&self, handle: &Handle) -> String263 fn handle(&self, handle: &Handle) -> String {
264 // Handles are either printed as `ResourceAny` for any guest-defined
265 // resource or `Resource<T>` for all host-defined resources. This means
266 // that this function needs to determine if `handle` points to a host
267 // or a guest resource which is determined by:
268 //
269 // * For world-owned resources, they're always imported.
270 // * For interface-owned resources, it depends on the how bindings were
271 // last generated for this interface.
272 //
273 // Additionally type aliases via `use` are "peeled" here to find the
274 // original definition of the resource since that's the one that we
275 // care about for determining whether it's imported or not.
276 let resource = match handle {
277 Handle::Own(t) | Handle::Borrow(t) => *t,
278 };
279 let ty = &self.resolve().types[resource];
280 let def_id = super::resolve_type_definition_id(self.resolve(), resource);
281 let ty_def = &self.resolve().types[def_id];
282 let is_host_defined = match ty_def.owner {
283 TypeOwner::Interface(i) => self.is_imported_interface(i),
284 _ => true,
285 };
286 let wt = self.wasmtime_path();
287 if is_host_defined {
288 let mut out = format!("{wt}::component::Resource<");
289 out.push_str(&self.type_name_in_interface(
290 ty.owner,
291 &ty.name.as_ref().unwrap().to_upper_camel_case(),
292 ));
293 out.push_str(">");
294 out
295 } else {
296 format!("{wt}::component::ResourceAny")
297 }
298 }
299
print_generics(&mut self, lifetime: Option<&str>)300 fn print_generics(&mut self, lifetime: Option<&str>) {
301 self.push_str(&self.generics(lifetime))
302 }
generics(&self, lifetime: Option<&str>) -> String303 fn generics(&self, lifetime: Option<&str>) -> String {
304 if let Some(lt) = lifetime {
305 format!("<{lt},>")
306 } else {
307 String::new()
308 }
309 }
310
modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)>311 fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
312 let info = self.info(ty);
313 // Info only populated for types that are passed to and from functions. For
314 // types which are not, default to the ownership setting.
315 if !info.owned && !info.borrowed {
316 return vec![(
317 self.param_name(ty),
318 match self.ownership() {
319 Ownership::Owning => TypeMode::Owned,
320 Ownership::Borrowing { .. } => TypeMode::AllBorrowed("'a"),
321 },
322 )];
323 }
324 let mut result = Vec::new();
325 let first_mode =
326 if info.owned || !info.borrowed || matches!(self.ownership(), Ownership::Owning) {
327 TypeMode::Owned
328 } else {
329 assert!(!self.uses_two_names(&info));
330 TypeMode::AllBorrowed("'a")
331 };
332 result.push((self.result_name(ty), first_mode));
333 if self.uses_two_names(&info) {
334 result.push((self.param_name(ty), TypeMode::AllBorrowed("'a")));
335 }
336 result
337 }
338
param_name(&self, ty: TypeId) -> String339 fn param_name(&self, ty: TypeId) -> String {
340 let info = self.info(ty);
341 let name = self.resolve().types[ty]
342 .name
343 .as_ref()
344 .unwrap()
345 .to_upper_camel_case();
346 if self.uses_two_names(&info) {
347 format!("{name}Param")
348 } else {
349 name
350 }
351 }
352
result_name(&self, ty: TypeId) -> String353 fn result_name(&self, ty: TypeId) -> String {
354 let info = self.info(ty);
355 let name = self.resolve().types[ty]
356 .name
357 .as_ref()
358 .unwrap()
359 .to_upper_camel_case();
360 if self.uses_two_names(&info) {
361 format!("{name}Result")
362 } else {
363 name
364 }
365 }
366
uses_two_names(&self, info: &TypeInfo) -> bool367 fn uses_two_names(&self, info: &TypeInfo) -> bool {
368 info.has_list
369 && info.borrowed
370 && info.owned
371 && matches!(
372 self.ownership(),
373 Ownership::Borrowing {
374 duplicate_if_necessary: true
375 }
376 )
377 }
378
lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str>379 fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {
380 if matches!(self.ownership(), Ownership::Owning) {
381 return None;
382 }
383 let lt = match mode {
384 TypeMode::AllBorrowed(s) => s,
385 _ => return None,
386 };
387 // No lifetimes needed unless this has a list.
388 if !info.has_list {
389 return None;
390 }
391 // If two names are used then this type will have an owned and a
392 // borrowed copy and the borrowed copy is being used, so it needs a
393 // lifetime. Otherwise if it's only borrowed and not owned then this can
394 // also use a lifetime since it's not needed in two contexts and only
395 // the borrowed version of the structure was generated.
396 if self.uses_two_names(info) || (info.borrowed && !info.owned) {
397 Some(lt)
398 } else {
399 None
400 }
401 }
402
typedfunc_sig(&self, func: &Function, param_mode: TypeMode) -> String403 fn typedfunc_sig(&self, func: &Function, param_mode: TypeMode) -> String {
404 let mut out = "(".to_string();
405 for param in func.params.iter() {
406 out.push_str(&self.ty(¶m.ty, param_mode));
407 out.push_str(", ");
408 }
409 out.push_str("), (");
410 if let Some(ty) = func.result {
411 out.push_str(&self.ty(&ty, TypeMode::Owned));
412 out.push_str(", ");
413 }
414 out.push_str(")");
415 out
416 }
417 }
418
419 /// Translate `name` to a Rust `snake_case` identifier.
to_rust_ident(name: &str) -> String420 pub fn to_rust_ident(name: &str) -> String {
421 match name {
422 // Escape Rust keywords.
423 // Source: https://doc.rust-lang.org/reference/keywords.html
424 "as" => "as_".into(),
425 "break" => "break_".into(),
426 "const" => "const_".into(),
427 "continue" => "continue_".into(),
428 "crate" => "crate_".into(),
429 "else" => "else_".into(),
430 "enum" => "enum_".into(),
431 "extern" => "extern_".into(),
432 "false" => "false_".into(),
433 "fn" => "fn_".into(),
434 "for" => "for_".into(),
435 "if" => "if_".into(),
436 "impl" => "impl_".into(),
437 "in" => "in_".into(),
438 "let" => "let_".into(),
439 "loop" => "loop_".into(),
440 "match" => "match_".into(),
441 "mod" => "mod_".into(),
442 "move" => "move_".into(),
443 "mut" => "mut_".into(),
444 "pub" => "pub_".into(),
445 "ref" => "ref_".into(),
446 "return" => "return_".into(),
447 "self" => "self_".into(),
448 "static" => "static_".into(),
449 "struct" => "struct_".into(),
450 "super" => "super_".into(),
451 "trait" => "trait_".into(),
452 "true" => "true_".into(),
453 "type" => "type_".into(),
454 "unsafe" => "unsafe_".into(),
455 "use" => "use_".into(),
456 "where" => "where_".into(),
457 "while" => "while_".into(),
458 "async" => "async_".into(),
459 "await" => "await_".into(),
460 "dyn" => "dyn_".into(),
461 "abstract" => "abstract_".into(),
462 "become" => "become_".into(),
463 "box" => "box_".into(),
464 "do" => "do_".into(),
465 "final" => "final_".into(),
466 "macro" => "macro_".into(),
467 "override" => "override_".into(),
468 "priv" => "priv_".into(),
469 "typeof" => "typeof_".into(),
470 "unsized" => "unsized_".into(),
471 "virtual" => "virtual_".into(),
472 "yield" => "yield_".into(),
473 "try" => "try_".into(),
474 "gen" => "gen_".into(),
475 s => s.to_snake_case(),
476 }
477 }
478
479 /// Translate `name` to a Rust `UpperCamelCase` identifier.
to_rust_upper_camel_case(name: &str) -> String480 pub fn to_rust_upper_camel_case(name: &str) -> String {
481 match name {
482 // We use `Host` as the name of the trait for host implementations
483 // to fill in, so rename it if "Host" is used as a regular identifier.
484 "host" => "Host_".into(),
485 s => s.to_upper_camel_case(),
486 }
487 }
488