Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/crates/wit-bindgen/src/rust.rs
1693 views
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> {
12
fn resolve(&self) -> &'a Resolve;
13
14
fn push_str(&mut self, s: &str);
15
fn info(&self, ty: TypeId) -> TypeInfo;
16
fn path_to_interface(&self, interface: InterfaceId) -> Option<String>;
17
fn is_imported_interface(&self, interface: InterfaceId) -> bool;
18
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.
29
fn ownership(&self) -> Ownership;
30
31
fn print_ty(&mut self, ty: &Type, mode: TypeMode) {
32
self.push_str(&self.ty(ty, mode))
33
}
34
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
69
fn print_optional_ty(&mut self, ty: Option<&Type>, mode: TypeMode) {
70
self.push_str(&self.optional_ty(ty, mode))
71
}
72
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
79
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::Flags(_)
125
| TypeDefKind::Enum(_)
126
| TypeDefKind::Tuple(_)
127
| TypeDefKind::Handle(_)
128
| TypeDefKind::Resource => true,
129
TypeDefKind::Type(Type::Id(t)) => {
130
needs_generics(resolve, &resolve.types[*t].kind)
131
}
132
TypeDefKind::Type(Type::String) => true,
133
TypeDefKind::Type(_) => false,
134
TypeDefKind::Unknown => unreachable!(),
135
TypeDefKind::FixedSizeList(..) => todo!(),
136
}
137
}
138
}
139
140
match &ty.kind {
141
TypeDefKind::List(t) => self.list(t, mode),
142
143
TypeDefKind::Option(t) => {
144
format!("Option<{}>", self.ty(t, mode))
145
}
146
147
TypeDefKind::Result(r) => {
148
let ok = self.optional_ty(r.ok.as_ref(), mode);
149
let err = self.optional_ty(r.err.as_ref(), mode);
150
format!("Result<{ok},{err}>")
151
}
152
153
TypeDefKind::Variant(_) => panic!("unsupported anonymous variant"),
154
155
// Tuple-like records are mapped directly to Rust tuples of
156
// types. Note the trailing comma after each member to
157
// appropriately handle 1-tuples.
158
TypeDefKind::Tuple(t) => {
159
let mut out = "(".to_string();
160
for ty in t.types.iter() {
161
out.push_str(&self.ty(ty, mode));
162
out.push_str(",");
163
}
164
out.push_str(")");
165
out
166
}
167
TypeDefKind::Record(_) => {
168
panic!("unsupported anonymous type reference: record")
169
}
170
TypeDefKind::Flags(_) => {
171
panic!("unsupported anonymous type reference: flags")
172
}
173
TypeDefKind::Enum(_) => {
174
panic!("unsupported anonymous type reference: enum")
175
}
176
TypeDefKind::Future(ty) => {
177
let wt = self.wasmtime_path();
178
let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
179
format!("{wt}::component::FutureReader<{t}>")
180
}
181
TypeDefKind::Stream(ty) => {
182
let wt = self.wasmtime_path();
183
let t = self.optional_ty(ty.as_ref(), TypeMode::Owned);
184
format!("{wt}::component::StreamReader<{t}>")
185
}
186
TypeDefKind::Handle(handle) => self.handle(handle),
187
TypeDefKind::Resource => unreachable!(),
188
189
TypeDefKind::Type(t) => self.ty(t, mode),
190
TypeDefKind::Unknown => unreachable!(),
191
TypeDefKind::FixedSizeList(..) => todo!(),
192
}
193
}
194
195
fn type_name_in_interface(&self, owner: TypeOwner, name: &str) -> String {
196
let mut out = String::new();
197
if let TypeOwner::Interface(id) = owner {
198
if let Some(path) = self.path_to_interface(id) {
199
out.push_str(&path);
200
out.push_str("::");
201
}
202
}
203
out.push_str(name);
204
out
205
}
206
207
fn print_list(&mut self, ty: &Type, mode: TypeMode) {
208
self.push_str(&self.list(ty, mode))
209
}
210
fn list(&self, ty: &Type, mode: TypeMode) -> String {
211
let next_mode = if matches!(self.ownership(), Ownership::Owning) {
212
TypeMode::Owned
213
} else {
214
mode
215
};
216
let ty = self.ty(ty, next_mode);
217
match mode {
218
TypeMode::AllBorrowed(lt) => {
219
if lt != "'_" {
220
format!("&{lt} [{ty}]")
221
} else {
222
format!("&[{ty}]")
223
}
224
}
225
TypeMode::Owned => {
226
let wt = self.wasmtime_path();
227
format!("{wt}::component::__internal::Vec<{ty}>")
228
}
229
}
230
}
231
232
fn print_stream(&mut self, ty: Option<&Type>) {
233
self.push_str(&self.stream(ty))
234
}
235
fn stream(&self, ty: Option<&Type>) -> String {
236
let wt = self.wasmtime_path();
237
let mut out = format!("{wt}::component::HostStream<");
238
out.push_str(&self.optional_ty(ty, TypeMode::Owned));
239
out.push_str(">");
240
out
241
}
242
243
fn print_future(&mut self, ty: Option<&Type>) {
244
self.push_str(&self.future(ty))
245
}
246
fn future(&self, ty: Option<&Type>) -> String {
247
let wt = self.wasmtime_path();
248
let mut out = format!("{wt}::component::HostFuture<");
249
out.push_str(&self.optional_ty(ty, TypeMode::Owned));
250
out.push_str(">");
251
out
252
}
253
254
fn print_handle(&mut self, handle: &Handle) {
255
self.push_str(&self.handle(handle))
256
}
257
fn handle(&self, handle: &Handle) -> String {
258
// Handles are either printed as `ResourceAny` for any guest-defined
259
// resource or `Resource<T>` for all host-defined resources. This means
260
// that this function needs to determine if `handle` points to a host
261
// or a guest resource which is determined by:
262
//
263
// * For world-owned resources, they're always imported.
264
// * For interface-owned resources, it depends on the how bindings were
265
// last generated for this interface.
266
//
267
// Additionally type aliases via `use` are "peeled" here to find the
268
// original definition of the resource since that's the one that we
269
// care about for determining whether it's imported or not.
270
let resource = match handle {
271
Handle::Own(t) | Handle::Borrow(t) => *t,
272
};
273
let ty = &self.resolve().types[resource];
274
let def_id = super::resolve_type_definition_id(self.resolve(), resource);
275
let ty_def = &self.resolve().types[def_id];
276
let is_host_defined = match ty_def.owner {
277
TypeOwner::Interface(i) => self.is_imported_interface(i),
278
_ => true,
279
};
280
let wt = self.wasmtime_path();
281
if is_host_defined {
282
let mut out = format!("{wt}::component::Resource<");
283
out.push_str(&self.type_name_in_interface(
284
ty.owner,
285
&ty.name.as_ref().unwrap().to_upper_camel_case(),
286
));
287
out.push_str(">");
288
out
289
} else {
290
format!("{wt}::component::ResourceAny")
291
}
292
}
293
294
fn print_generics(&mut self, lifetime: Option<&str>) {
295
self.push_str(&self.generics(lifetime))
296
}
297
fn generics(&self, lifetime: Option<&str>) -> String {
298
if let Some(lt) = lifetime {
299
format!("<{lt},>")
300
} else {
301
String::new()
302
}
303
}
304
305
fn modes_of(&self, ty: TypeId) -> Vec<(String, TypeMode)> {
306
let info = self.info(ty);
307
// Info only populated for types that are passed to and from functions. For
308
// types which are not, default to the ownership setting.
309
if !info.owned && !info.borrowed {
310
return vec![(
311
self.param_name(ty),
312
match self.ownership() {
313
Ownership::Owning => TypeMode::Owned,
314
Ownership::Borrowing { .. } => TypeMode::AllBorrowed("'a"),
315
},
316
)];
317
}
318
let mut result = Vec::new();
319
let first_mode =
320
if info.owned || !info.borrowed || matches!(self.ownership(), Ownership::Owning) {
321
TypeMode::Owned
322
} else {
323
assert!(!self.uses_two_names(&info));
324
TypeMode::AllBorrowed("'a")
325
};
326
result.push((self.result_name(ty), first_mode));
327
if self.uses_two_names(&info) {
328
result.push((self.param_name(ty), TypeMode::AllBorrowed("'a")));
329
}
330
result
331
}
332
333
fn param_name(&self, ty: TypeId) -> String {
334
let info = self.info(ty);
335
let name = self.resolve().types[ty]
336
.name
337
.as_ref()
338
.unwrap()
339
.to_upper_camel_case();
340
if self.uses_two_names(&info) {
341
format!("{name}Param")
342
} else {
343
name
344
}
345
}
346
347
fn result_name(&self, ty: TypeId) -> String {
348
let info = self.info(ty);
349
let name = self.resolve().types[ty]
350
.name
351
.as_ref()
352
.unwrap()
353
.to_upper_camel_case();
354
if self.uses_two_names(&info) {
355
format!("{name}Result")
356
} else {
357
name
358
}
359
}
360
361
fn uses_two_names(&self, info: &TypeInfo) -> bool {
362
info.has_list
363
&& info.borrowed
364
&& info.owned
365
&& matches!(
366
self.ownership(),
367
Ownership::Borrowing {
368
duplicate_if_necessary: true
369
}
370
)
371
}
372
373
fn lifetime_for(&self, info: &TypeInfo, mode: TypeMode) -> Option<&'static str> {
374
if matches!(self.ownership(), Ownership::Owning) {
375
return None;
376
}
377
let lt = match mode {
378
TypeMode::AllBorrowed(s) => s,
379
_ => return None,
380
};
381
// No lifetimes needed unless this has a list.
382
if !info.has_list {
383
return None;
384
}
385
// If two names are used then this type will have an owned and a
386
// borrowed copy and the borrowed copy is being used, so it needs a
387
// lifetime. Otherwise if it's only borrowed and not owned then this can
388
// also use a lifetime since it's not needed in two contexts and only
389
// the borrowed version of the structure was generated.
390
if self.uses_two_names(info) || (info.borrowed && !info.owned) {
391
Some(lt)
392
} else {
393
None
394
}
395
}
396
397
fn typedfunc_sig(&self, func: &Function, param_mode: TypeMode) -> String {
398
let mut out = "(".to_string();
399
for (_, ty) in func.params.iter() {
400
out.push_str(&self.ty(ty, param_mode));
401
out.push_str(", ");
402
}
403
out.push_str("), (");
404
if let Some(ty) = func.result {
405
out.push_str(&self.ty(&ty, TypeMode::Owned));
406
out.push_str(", ");
407
}
408
out.push_str(")");
409
out
410
}
411
}
412
413
/// Translate `name` to a Rust `snake_case` identifier.
414
pub fn to_rust_ident(name: &str) -> String {
415
match name {
416
// Escape Rust keywords.
417
// Source: https://doc.rust-lang.org/reference/keywords.html
418
"as" => "as_".into(),
419
"break" => "break_".into(),
420
"const" => "const_".into(),
421
"continue" => "continue_".into(),
422
"crate" => "crate_".into(),
423
"else" => "else_".into(),
424
"enum" => "enum_".into(),
425
"extern" => "extern_".into(),
426
"false" => "false_".into(),
427
"fn" => "fn_".into(),
428
"for" => "for_".into(),
429
"if" => "if_".into(),
430
"impl" => "impl_".into(),
431
"in" => "in_".into(),
432
"let" => "let_".into(),
433
"loop" => "loop_".into(),
434
"match" => "match_".into(),
435
"mod" => "mod_".into(),
436
"move" => "move_".into(),
437
"mut" => "mut_".into(),
438
"pub" => "pub_".into(),
439
"ref" => "ref_".into(),
440
"return" => "return_".into(),
441
"self" => "self_".into(),
442
"static" => "static_".into(),
443
"struct" => "struct_".into(),
444
"super" => "super_".into(),
445
"trait" => "trait_".into(),
446
"true" => "true_".into(),
447
"type" => "type_".into(),
448
"unsafe" => "unsafe_".into(),
449
"use" => "use_".into(),
450
"where" => "where_".into(),
451
"while" => "while_".into(),
452
"async" => "async_".into(),
453
"await" => "await_".into(),
454
"dyn" => "dyn_".into(),
455
"abstract" => "abstract_".into(),
456
"become" => "become_".into(),
457
"box" => "box_".into(),
458
"do" => "do_".into(),
459
"final" => "final_".into(),
460
"macro" => "macro_".into(),
461
"override" => "override_".into(),
462
"priv" => "priv_".into(),
463
"typeof" => "typeof_".into(),
464
"unsized" => "unsized_".into(),
465
"virtual" => "virtual_".into(),
466
"yield" => "yield_".into(),
467
"try" => "try_".into(),
468
"gen" => "gen_".into(),
469
s => s.to_snake_case(),
470
}
471
}
472
473
/// Translate `name` to a Rust `UpperCamelCase` identifier.
474
pub fn to_rust_upper_camel_case(name: &str) -> String {
475
match name {
476
// We use `Host` as the name of the trait for host implementations
477
// to fill in, so rename it if "Host" is used as a regular identifier.
478
"host" => "Host_".into(),
479
s => s.to_upper_camel_case(),
480
}
481
}
482
483