1 | use anyhow::{anyhow, bail, Result}; |
2 | use std::borrow::Cow; |
3 | use std::collections::HashMap; |
4 | use std::fmt::Display; |
5 | use std::mem; |
6 | use std::ops::Deref; |
7 | use wit_parser::*; |
8 | |
9 | // NB: keep in sync with `crates/wit-parser/src/ast/lex.rs` |
10 | const PRINT_F32_F64_DEFAULT: bool = true; |
11 | |
12 | /// A utility for printing WebAssembly interface definitions to a string. |
13 | pub struct WitPrinter<O: Output = OutputToString> { |
14 | /// Visitor that holds the WIT document being printed. |
15 | pub output: O, |
16 | |
17 | // Count of how many items in this current block have been printed to print |
18 | // a blank line between each item, but not the first item. |
19 | any_items: bool, |
20 | |
21 | // Whether to print doc comments. |
22 | emit_docs: bool, |
23 | |
24 | print_f32_f64: bool, |
25 | } |
26 | |
27 | impl Default for WitPrinter { |
28 | fn default() -> Self { |
29 | Self::new(output:OutputToString::default()) |
30 | } |
31 | } |
32 | |
33 | impl<O: Output> WitPrinter<O> { |
34 | /// Craete new instance. |
35 | pub fn new(output: O) -> Self { |
36 | Self { |
37 | output, |
38 | any_items: false, |
39 | emit_docs: true, |
40 | print_f32_f64: match std::env::var("WIT_REQUIRE_F32_F64" ) { |
41 | Ok(s) => s == "1" , |
42 | Err(_) => PRINT_F32_F64_DEFAULT, |
43 | }, |
44 | } |
45 | } |
46 | |
47 | /// Prints the specified `pkg` which is located in `resolve` to `O`. |
48 | /// |
49 | /// The `nested` list of packages are other packages to include at the end |
50 | /// of the output in `package ... { ... }` syntax. |
51 | pub fn print(&mut self, resolve: &Resolve, pkg: PackageId, nested: &[PackageId]) -> Result<()> { |
52 | self.print_package(resolve, pkg, true)?; |
53 | for (i, pkg_id) in nested.iter().enumerate() { |
54 | if i > 0 { |
55 | self.output.newline(); |
56 | self.output.newline(); |
57 | } |
58 | self.print_package(resolve, *pkg_id, false)?; |
59 | } |
60 | Ok(()) |
61 | } |
62 | |
63 | /// Configure whether doc comments will be printed. |
64 | /// |
65 | /// Defaults to true. |
66 | pub fn emit_docs(&mut self, enabled: bool) -> &mut Self { |
67 | self.emit_docs = enabled; |
68 | self |
69 | } |
70 | |
71 | /// Prints the specified `pkg`. |
72 | /// |
73 | /// If `is_main` is not set, nested package notation is used. |
74 | pub fn print_package( |
75 | &mut self, |
76 | resolve: &Resolve, |
77 | pkg: PackageId, |
78 | is_main: bool, |
79 | ) -> Result<()> { |
80 | let pkg = &resolve.packages[pkg]; |
81 | self.print_package_outer(pkg)?; |
82 | |
83 | if is_main { |
84 | self.output.semicolon(); |
85 | self.output.newline(); |
86 | } else { |
87 | self.output.indent_start(); |
88 | } |
89 | |
90 | for (name, id) in pkg.interfaces.iter() { |
91 | self.print_interface_outer(resolve, *id, name)?; |
92 | self.output.indent_start(); |
93 | self.print_interface(resolve, *id)?; |
94 | self.output.indent_end(); |
95 | if is_main { |
96 | self.output.newline(); |
97 | } |
98 | } |
99 | |
100 | for (name, id) in pkg.worlds.iter() { |
101 | self.print_docs(&resolve.worlds[*id].docs); |
102 | self.print_stability(&resolve.worlds[*id].stability); |
103 | self.output.keyword("world" ); |
104 | self.output.str(" " ); |
105 | self.print_name_type(name, TypeKind::WorldDeclaration); |
106 | self.output.indent_start(); |
107 | self.print_world(resolve, *id)?; |
108 | self.output.indent_end(); |
109 | } |
110 | if !is_main { |
111 | self.output.indent_end(); |
112 | } |
113 | Ok(()) |
114 | } |
115 | |
116 | /// Print the specified package without its content. |
117 | /// Does not print the semicolon nor starts the indentation. |
118 | pub fn print_package_outer(&mut self, pkg: &Package) -> Result<()> { |
119 | self.print_docs(&pkg.docs); |
120 | self.output.keyword("package" ); |
121 | self.output.str(" " ); |
122 | self.print_name_type(&pkg.name.namespace, TypeKind::NamespaceDeclaration); |
123 | self.output.str(":" ); |
124 | self.print_name_type(&pkg.name.name, TypeKind::PackageNameDeclaration); |
125 | if let Some(version) = &pkg.name.version { |
126 | self.print_name_type(&format!("@ {version}" ), TypeKind::VersionDeclaration); |
127 | } |
128 | Ok(()) |
129 | } |
130 | |
131 | fn new_item(&mut self) { |
132 | if self.any_items { |
133 | self.output.newline(); |
134 | } |
135 | self.any_items = true; |
136 | } |
137 | |
138 | /// Print the given WebAssembly interface without its content. |
139 | /// Does not print the semicolon nor starts the indentation. |
140 | pub fn print_interface_outer( |
141 | &mut self, |
142 | resolve: &Resolve, |
143 | id: InterfaceId, |
144 | name: &str, |
145 | ) -> Result<()> { |
146 | self.print_docs(&resolve.interfaces[id].docs); |
147 | self.print_stability(&resolve.interfaces[id].stability); |
148 | self.output.keyword("interface" ); |
149 | self.output.str(" " ); |
150 | self.print_name_type(name, TypeKind::InterfaceDeclaration); |
151 | Ok(()) |
152 | } |
153 | |
154 | /// Print the inner content of a given WebAssembly interface. |
155 | pub fn print_interface(&mut self, resolve: &Resolve, id: InterfaceId) -> Result<()> { |
156 | let prev_items = mem::replace(&mut self.any_items, false); |
157 | let interface = &resolve.interfaces[id]; |
158 | |
159 | let mut resource_funcs = HashMap::new(); |
160 | let mut freestanding = Vec::new(); |
161 | for (name, func) in interface.functions.iter() { |
162 | if let Some(id) = resource_func(func) { |
163 | resource_funcs.entry(id).or_insert(Vec::new()).push(func); |
164 | } else { |
165 | freestanding.push((name, func)); |
166 | } |
167 | } |
168 | |
169 | self.print_types( |
170 | resolve, |
171 | TypeOwner::Interface(id), |
172 | interface |
173 | .types |
174 | .iter() |
175 | .map(|(name, id)| (name.as_str(), *id)), |
176 | &resource_funcs, |
177 | )?; |
178 | |
179 | for (name, func) in freestanding { |
180 | self.new_item(); |
181 | self.print_docs(&func.docs); |
182 | self.print_stability(&func.stability); |
183 | self.print_name_type(name, TypeKind::FunctionFreestanding); |
184 | self.output.str(": " ); |
185 | self.print_function(resolve, func)?; |
186 | self.output.semicolon(); |
187 | } |
188 | |
189 | self.any_items = prev_items; |
190 | |
191 | Ok(()) |
192 | } |
193 | |
194 | /// Print types of an interface. |
195 | pub fn print_types<'a>( |
196 | &mut self, |
197 | resolve: &Resolve, |
198 | owner: TypeOwner, |
199 | types: impl Iterator<Item = (&'a str, TypeId)>, |
200 | resource_funcs: &HashMap<TypeId, Vec<&Function>>, |
201 | ) -> Result<()> { |
202 | // Partition types defined in this interface into either those imported |
203 | // from foreign interfaces or those defined locally. |
204 | let mut types_to_declare = Vec::new(); |
205 | let mut types_to_import: Vec<(_, &_, Vec<_>)> = Vec::new(); |
206 | for (name, ty_id) in types { |
207 | let ty = &resolve.types[ty_id]; |
208 | if let TypeDefKind::Type(Type::Id(other)) = ty.kind { |
209 | let other = &resolve.types[other]; |
210 | match other.owner { |
211 | TypeOwner::None => {} |
212 | other_owner if owner != other_owner => { |
213 | let other_name = other |
214 | .name |
215 | .as_ref() |
216 | .ok_or_else(|| anyhow!("cannot import unnamed type" ))?; |
217 | if let Some((owner, stability, list)) = types_to_import.last_mut() { |
218 | if *owner == other_owner && ty.stability == **stability { |
219 | list.push((name, other_name)); |
220 | continue; |
221 | } |
222 | } |
223 | types_to_import.push(( |
224 | other_owner, |
225 | &ty.stability, |
226 | vec![(name, other_name)], |
227 | )); |
228 | continue; |
229 | } |
230 | _ => {} |
231 | } |
232 | } |
233 | |
234 | types_to_declare.push(ty_id); |
235 | } |
236 | |
237 | // Generate a `use` statement for all imported types. |
238 | let my_pkg = match owner { |
239 | TypeOwner::Interface(id) => resolve.interfaces[id].package.unwrap(), |
240 | TypeOwner::World(id) => resolve.worlds[id].package.unwrap(), |
241 | TypeOwner::None => unreachable!(), |
242 | }; |
243 | for (owner, stability, tys) in types_to_import { |
244 | self.any_items = true; |
245 | self.print_stability(stability); |
246 | self.output.keyword("use" ); |
247 | self.output.str(" " ); |
248 | let id = match owner { |
249 | TypeOwner::Interface(id) => id, |
250 | // it's only possible to import types from interfaces at |
251 | // this time. |
252 | _ => unreachable!(), |
253 | }; |
254 | self.print_path_to_interface(resolve, id, my_pkg)?; |
255 | self.output.str(".{" ); // Note: not changing the indentation. |
256 | for (i, (my_name, other_name)) in tys.into_iter().enumerate() { |
257 | if i > 0 { |
258 | self.output.str(", " ); |
259 | } |
260 | if my_name == other_name { |
261 | self.print_name_type(my_name, TypeKind::TypeImport); |
262 | } else { |
263 | self.print_name_type(other_name, TypeKind::TypeImport); |
264 | self.output.str(" " ); |
265 | self.output.keyword("as" ); |
266 | self.output.str(" " ); |
267 | self.print_name_type(my_name, TypeKind::TypeAlias); |
268 | } |
269 | } |
270 | self.output.str("}" ); // Note: not changing the indentation. |
271 | self.output.semicolon(); |
272 | } |
273 | |
274 | for id in types_to_declare { |
275 | self.new_item(); |
276 | self.print_docs(&resolve.types[id].docs); |
277 | self.print_stability(&resolve.types[id].stability); |
278 | match resolve.types[id].kind { |
279 | TypeDefKind::Resource => self.print_resource( |
280 | resolve, |
281 | id, |
282 | resource_funcs.get(&id).unwrap_or(&Vec::new()), |
283 | )?, |
284 | _ => self.declare_type(resolve, &Type::Id(id))?, |
285 | } |
286 | } |
287 | |
288 | Ok(()) |
289 | } |
290 | |
291 | fn print_resource(&mut self, resolve: &Resolve, id: TypeId, funcs: &[&Function]) -> Result<()> { |
292 | let ty = &resolve.types[id]; |
293 | self.output.ty("resource" , TypeKind::BuiltIn); |
294 | self.output.str(" " ); |
295 | self.print_name_type( |
296 | ty.name.as_ref().expect("resources must be named" ), |
297 | TypeKind::Resource, |
298 | ); |
299 | if funcs.is_empty() { |
300 | self.output.semicolon(); |
301 | return Ok(()); |
302 | } |
303 | self.output.indent_start(); |
304 | for func in funcs { |
305 | self.print_docs(&func.docs); |
306 | self.print_stability(&func.stability); |
307 | |
308 | match &func.kind { |
309 | FunctionKind::Constructor(_) => {} |
310 | FunctionKind::Method(_) => { |
311 | self.print_name_type(func.item_name(), TypeKind::FunctionMethod); |
312 | self.output.str(": " ); |
313 | } |
314 | FunctionKind::Static(_) => { |
315 | self.print_name_type(func.item_name(), TypeKind::FunctionStatic); |
316 | self.output.str(": " ); |
317 | self.output.keyword("static" ); |
318 | self.output.str(" " ); |
319 | } |
320 | FunctionKind::Freestanding => unreachable!(), |
321 | } |
322 | self.print_function(resolve, func)?; |
323 | self.output.semicolon(); |
324 | } |
325 | self.output.indent_end(); |
326 | |
327 | Ok(()) |
328 | } |
329 | |
330 | fn print_function(&mut self, resolve: &Resolve, func: &Function) -> Result<()> { |
331 | // Constructors are named slightly differently. |
332 | match &func.kind { |
333 | FunctionKind::Constructor(_) => { |
334 | self.output.keyword("constructor" ); |
335 | self.output.str("(" ); |
336 | } |
337 | _ => { |
338 | self.output.keyword("func" ); |
339 | self.output.str("(" ); |
340 | } |
341 | } |
342 | |
343 | // Methods don't print their `self` argument |
344 | let params_to_skip = match &func.kind { |
345 | FunctionKind::Method(_) => 1, |
346 | _ => 0, |
347 | }; |
348 | for (i, (name, ty)) in func.params.iter().skip(params_to_skip).enumerate() { |
349 | if i > 0 { |
350 | self.output.str(", " ); |
351 | } |
352 | self.print_name_param(name); |
353 | self.output.str(": " ); |
354 | self.print_type_name(resolve, ty)?; |
355 | } |
356 | self.output.str(")" ); |
357 | |
358 | // constructors don't have their results printed |
359 | if let FunctionKind::Constructor(_) = func.kind { |
360 | return Ok(()); |
361 | } |
362 | |
363 | match &func.results { |
364 | Results::Named(rs) => match rs.len() { |
365 | 0 => (), |
366 | _ => { |
367 | self.output.str(" -> (" ); |
368 | for (i, (name, ty)) in rs.iter().enumerate() { |
369 | if i > 0 { |
370 | self.output.str(", " ); |
371 | } |
372 | self.print_name_param(name); |
373 | self.output.str(": " ); |
374 | self.print_type_name(resolve, ty)?; |
375 | } |
376 | self.output.str(")" ); |
377 | } |
378 | }, |
379 | Results::Anon(ty) => { |
380 | self.output.str(" -> " ); |
381 | self.print_type_name(resolve, ty)?; |
382 | } |
383 | } |
384 | Ok(()) |
385 | } |
386 | |
387 | fn print_world(&mut self, resolve: &Resolve, id: WorldId) -> Result<()> { |
388 | let prev_items = mem::replace(&mut self.any_items, false); |
389 | let world = &resolve.worlds[id]; |
390 | let pkgid = world.package.unwrap(); |
391 | let mut types = Vec::new(); |
392 | let mut resource_funcs = HashMap::new(); |
393 | for (name, import) in world.imports.iter() { |
394 | match import { |
395 | WorldItem::Type(t) => match name { |
396 | WorldKey::Name(s) => types.push((s.as_str(), *t)), |
397 | WorldKey::Interface(_) => unreachable!(), |
398 | }, |
399 | _ => { |
400 | if let WorldItem::Function(f) = import { |
401 | if let Some(id) = resource_func(f) { |
402 | resource_funcs.entry(id).or_insert(Vec::new()).push(f); |
403 | continue; |
404 | } |
405 | } |
406 | self.print_world_item(resolve, name, import, pkgid, "import" )?; |
407 | // Don't put a blank line between imports, but count |
408 | // imports as having printed something so if anything comes |
409 | // after them then a blank line is printed after imports. |
410 | self.any_items = true; |
411 | } |
412 | } |
413 | } |
414 | self.print_types( |
415 | resolve, |
416 | TypeOwner::World(id), |
417 | types.into_iter(), |
418 | &resource_funcs, |
419 | )?; |
420 | if !world.exports.is_empty() { |
421 | self.new_item(); |
422 | } |
423 | for (name, export) in world.exports.iter() { |
424 | self.print_world_item(resolve, name, export, pkgid, "export" )?; |
425 | } |
426 | self.any_items = prev_items; |
427 | Ok(()) |
428 | } |
429 | |
430 | fn print_world_item( |
431 | &mut self, |
432 | resolve: &Resolve, |
433 | name: &WorldKey, |
434 | item: &WorldItem, |
435 | cur_pkg: PackageId, |
436 | import_or_export_keyword: &str, |
437 | ) -> Result<()> { |
438 | // Print inline item docs |
439 | if matches!(name, WorldKey::Name(_)) { |
440 | self.print_docs(match item { |
441 | WorldItem::Interface { id, .. } => &resolve.interfaces[*id].docs, |
442 | WorldItem::Function(f) => &f.docs, |
443 | // Types are handled separately |
444 | WorldItem::Type(_) => unreachable!(), |
445 | }); |
446 | } |
447 | |
448 | self.print_stability(item.stability(resolve)); |
449 | self.output.keyword(import_or_export_keyword); |
450 | self.output.str(" " ); |
451 | match name { |
452 | WorldKey::Name(name) => { |
453 | self.print_name_type(name, TypeKind::Other); |
454 | self.output.str(": " ); |
455 | match item { |
456 | WorldItem::Interface { id, .. } => { |
457 | assert!(resolve.interfaces[*id].name.is_none()); |
458 | self.output.keyword("interface" ); |
459 | self.output.indent_start(); |
460 | self.print_interface(resolve, *id)?; |
461 | self.output.indent_end(); |
462 | } |
463 | WorldItem::Function(f) => { |
464 | self.print_function(resolve, f)?; |
465 | self.output.semicolon(); |
466 | } |
467 | // Types are handled separately |
468 | WorldItem::Type(_) => unreachable!(), |
469 | } |
470 | } |
471 | WorldKey::Interface(id) => { |
472 | match item { |
473 | WorldItem::Interface { id: id2, .. } => assert_eq!(id, id2), |
474 | _ => unreachable!(), |
475 | } |
476 | self.print_path_to_interface(resolve, *id, cur_pkg)?; |
477 | self.output.semicolon(); |
478 | } |
479 | } |
480 | Ok(()) |
481 | } |
482 | |
483 | fn print_path_to_interface( |
484 | &mut self, |
485 | resolve: &Resolve, |
486 | interface: InterfaceId, |
487 | cur_pkg: PackageId, |
488 | ) -> Result<()> { |
489 | let iface = &resolve.interfaces[interface]; |
490 | if iface.package == Some(cur_pkg) { |
491 | self.print_name_type(iface.name.as_ref().unwrap(), TypeKind::InterfacePath); |
492 | } else { |
493 | let pkg = &resolve.packages[iface.package.unwrap()].name; |
494 | self.print_name_type(&pkg.namespace, TypeKind::NamespacePath); |
495 | self.output.str(":" ); |
496 | self.print_name_type(&pkg.name, TypeKind::PackageNamePath); |
497 | self.output.str("/" ); |
498 | self.print_name_type(iface.name.as_ref().unwrap(), TypeKind::InterfacePath); |
499 | if let Some(version) = &pkg.version { |
500 | self.print_name_type(&format!("@ {version}" ), TypeKind::VersionPath); |
501 | } |
502 | } |
503 | Ok(()) |
504 | } |
505 | |
506 | /// Print the name of type `ty`. |
507 | pub fn print_type_name(&mut self, resolve: &Resolve, ty: &Type) -> Result<()> { |
508 | match ty { |
509 | Type::Bool => self.output.ty("bool" , TypeKind::BuiltIn), |
510 | Type::U8 => self.output.ty("u8" , TypeKind::BuiltIn), |
511 | Type::U16 => self.output.ty("u16" , TypeKind::BuiltIn), |
512 | Type::U32 => self.output.ty("u32" , TypeKind::BuiltIn), |
513 | Type::U64 => self.output.ty("u64" , TypeKind::BuiltIn), |
514 | Type::S8 => self.output.ty("s8" , TypeKind::BuiltIn), |
515 | Type::S16 => self.output.ty("s16" , TypeKind::BuiltIn), |
516 | Type::S32 => self.output.ty("s32" , TypeKind::BuiltIn), |
517 | Type::S64 => self.output.ty("s64" , TypeKind::BuiltIn), |
518 | Type::F32 => { |
519 | if self.print_f32_f64 { |
520 | self.output.ty("f32" , TypeKind::BuiltIn) |
521 | } else { |
522 | self.output.ty("f32" , TypeKind::BuiltIn) |
523 | } |
524 | } |
525 | Type::F64 => { |
526 | if self.print_f32_f64 { |
527 | self.output.ty("f64" , TypeKind::BuiltIn) |
528 | } else { |
529 | self.output.ty("f64" , TypeKind::BuiltIn) |
530 | } |
531 | } |
532 | Type::Char => self.output.ty("char" , TypeKind::BuiltIn), |
533 | Type::String => self.output.ty("string" , TypeKind::BuiltIn), |
534 | |
535 | Type::Id(id) => { |
536 | let ty = &resolve.types[*id]; |
537 | if let Some(name) = &ty.name { |
538 | self.print_name_type(name, TypeKind::Other); |
539 | return Ok(()); |
540 | } |
541 | |
542 | match &ty.kind { |
543 | TypeDefKind::Handle(h) => { |
544 | self.print_handle_type(resolve, h, false)?; |
545 | } |
546 | TypeDefKind::Resource => { |
547 | bail!("resolve has an unnamed resource type" ); |
548 | } |
549 | TypeDefKind::Tuple(t) => { |
550 | self.print_tuple_type(resolve, t)?; |
551 | } |
552 | TypeDefKind::Option(t) => { |
553 | self.print_option_type(resolve, t)?; |
554 | } |
555 | TypeDefKind::Result(t) => { |
556 | self.print_result_type(resolve, t)?; |
557 | } |
558 | TypeDefKind::Record(_) => { |
559 | bail!("resolve has an unnamed record type" ); |
560 | } |
561 | TypeDefKind::Flags(_) => { |
562 | bail!("resolve has unnamed flags type" ) |
563 | } |
564 | TypeDefKind::Enum(_) => { |
565 | bail!("resolve has unnamed enum type" ) |
566 | } |
567 | TypeDefKind::Variant(_) => { |
568 | bail!("resolve has unnamed variant type" ) |
569 | } |
570 | TypeDefKind::List(ty) => { |
571 | self.output.ty("list" , TypeKind::BuiltIn); |
572 | self.output.generic_args_start(); |
573 | self.print_type_name(resolve, ty)?; |
574 | self.output.generic_args_end(); |
575 | } |
576 | TypeDefKind::Type(ty) => self.print_type_name(resolve, ty)?, |
577 | TypeDefKind::Future(ty) => { |
578 | if let Some(ty) = ty { |
579 | self.output.push_str("future<" ); |
580 | self.print_type_name(resolve, ty)?; |
581 | self.output.push_str(">" ); |
582 | } else { |
583 | self.output.push_str("future" ); |
584 | } |
585 | } |
586 | TypeDefKind::Stream(ty) => { |
587 | self.output.push_str("stream<" ); |
588 | self.print_type_name(resolve, ty)?; |
589 | self.output.push_str(">" ); |
590 | } |
591 | TypeDefKind::ErrorContext => self.output.push_str("error-context" ), |
592 | TypeDefKind::Unknown => unreachable!(), |
593 | } |
594 | } |
595 | } |
596 | |
597 | Ok(()) |
598 | } |
599 | |
600 | fn print_handle_type( |
601 | &mut self, |
602 | resolve: &Resolve, |
603 | handle: &Handle, |
604 | force_handle_type_printed: bool, |
605 | ) -> Result<()> { |
606 | match handle { |
607 | Handle::Own(ty) => { |
608 | let ty = &resolve.types[*ty]; |
609 | if force_handle_type_printed { |
610 | self.output.ty("own" , TypeKind::BuiltIn); |
611 | self.output.generic_args_start(); |
612 | } |
613 | self.print_name_type( |
614 | ty.name |
615 | .as_ref() |
616 | .ok_or_else(|| anyhow!("unnamed resource type" ))?, |
617 | TypeKind::Resource, |
618 | ); |
619 | if force_handle_type_printed { |
620 | self.output.generic_args_end(); |
621 | } |
622 | } |
623 | |
624 | Handle::Borrow(ty) => { |
625 | self.output.ty("borrow" , TypeKind::BuiltIn); |
626 | self.output.generic_args_start(); |
627 | let ty = &resolve.types[*ty]; |
628 | self.print_name_type( |
629 | ty.name |
630 | .as_ref() |
631 | .ok_or_else(|| anyhow!("unnamed resource type" ))?, |
632 | TypeKind::Resource, |
633 | ); |
634 | self.output.generic_args_end(); |
635 | } |
636 | } |
637 | |
638 | Ok(()) |
639 | } |
640 | |
641 | fn print_tuple_type(&mut self, resolve: &Resolve, tuple: &Tuple) -> Result<()> { |
642 | self.output.ty("tuple" , TypeKind::BuiltIn); |
643 | self.output.generic_args_start(); |
644 | for (i, ty) in tuple.types.iter().enumerate() { |
645 | if i > 0 { |
646 | self.output.str(", " ); |
647 | } |
648 | self.print_type_name(resolve, ty)?; |
649 | } |
650 | self.output.generic_args_end(); |
651 | |
652 | Ok(()) |
653 | } |
654 | |
655 | fn print_option_type(&mut self, resolve: &Resolve, payload: &Type) -> Result<()> { |
656 | self.output.ty("option" , TypeKind::BuiltIn); |
657 | self.output.generic_args_start(); |
658 | self.print_type_name(resolve, payload)?; |
659 | self.output.generic_args_end(); |
660 | Ok(()) |
661 | } |
662 | |
663 | fn print_result_type(&mut self, resolve: &Resolve, result: &Result_) -> Result<()> { |
664 | match result { |
665 | Result_ { |
666 | ok: Some(ok), |
667 | err: Some(err), |
668 | } => { |
669 | self.output.ty("result" , TypeKind::BuiltIn); |
670 | self.output.generic_args_start(); |
671 | self.print_type_name(resolve, ok)?; |
672 | self.output.str(", " ); |
673 | self.print_type_name(resolve, err)?; |
674 | self.output.generic_args_end(); |
675 | } |
676 | Result_ { |
677 | ok: None, |
678 | err: Some(err), |
679 | } => { |
680 | self.output.ty("result" , TypeKind::BuiltIn); |
681 | self.output.generic_args_start(); |
682 | self.output.str("_, " ); |
683 | self.print_type_name(resolve, err)?; |
684 | self.output.generic_args_end(); |
685 | } |
686 | Result_ { |
687 | ok: Some(ok), |
688 | err: None, |
689 | } => { |
690 | self.output.ty("result" , TypeKind::BuiltIn); |
691 | self.output.generic_args_start(); |
692 | self.print_type_name(resolve, ok)?; |
693 | self.output.generic_args_end(); |
694 | } |
695 | Result_ { |
696 | ok: None, |
697 | err: None, |
698 | } => { |
699 | self.output.ty("result" , TypeKind::BuiltIn); |
700 | } |
701 | } |
702 | Ok(()) |
703 | } |
704 | |
705 | fn declare_type(&mut self, resolve: &Resolve, ty: &Type) -> Result<()> { |
706 | match ty { |
707 | Type::Bool |
708 | | Type::U8 |
709 | | Type::U16 |
710 | | Type::U32 |
711 | | Type::U64 |
712 | | Type::S8 |
713 | | Type::S16 |
714 | | Type::S32 |
715 | | Type::S64 |
716 | | Type::F32 |
717 | | Type::F64 |
718 | | Type::Char |
719 | | Type::String => return Ok(()), |
720 | |
721 | Type::Id(id) => { |
722 | let ty = &resolve.types[*id]; |
723 | match &ty.kind { |
724 | TypeDefKind::Handle(h) => { |
725 | self.declare_handle(resolve, ty.name.as_deref(), h)? |
726 | } |
727 | TypeDefKind::Resource => panic!("resources should be processed separately" ), |
728 | TypeDefKind::Record(r) => { |
729 | self.declare_record(resolve, ty.name.as_deref(), r)? |
730 | } |
731 | TypeDefKind::Tuple(t) => self.declare_tuple(resolve, ty.name.as_deref(), t)?, |
732 | TypeDefKind::Flags(f) => self.declare_flags(ty.name.as_deref(), f)?, |
733 | TypeDefKind::Variant(v) => { |
734 | self.declare_variant(resolve, ty.name.as_deref(), v)? |
735 | } |
736 | TypeDefKind::Option(t) => { |
737 | self.declare_option(resolve, ty.name.as_deref(), t)? |
738 | } |
739 | TypeDefKind::Result(r) => { |
740 | self.declare_result(resolve, ty.name.as_deref(), r)? |
741 | } |
742 | TypeDefKind::Enum(e) => self.declare_enum(ty.name.as_deref(), e)?, |
743 | TypeDefKind::List(inner) => { |
744 | self.declare_list(resolve, ty.name.as_deref(), inner)? |
745 | } |
746 | TypeDefKind::Type(inner) => match ty.name.as_deref() { |
747 | Some(name) => { |
748 | self.output.keyword("type" ); |
749 | self.output.str(" " ); |
750 | self.print_name_type(name, TypeKind::TypeName); |
751 | self.output.str(" = " ); |
752 | self.print_type_name(resolve, inner)?; |
753 | self.output.semicolon(); |
754 | } |
755 | None => bail!("unnamed type in document" ), |
756 | }, |
757 | TypeDefKind::Future(inner) => { |
758 | self.declare_future(resolve, ty.name.as_deref(), inner.as_ref())? |
759 | } |
760 | TypeDefKind::Stream(inner) => { |
761 | self.declare_stream(resolve, ty.name.as_deref(), inner)? |
762 | } |
763 | TypeDefKind::ErrorContext => self.declare_error_context(ty.name.as_deref())?, |
764 | TypeDefKind::Unknown => unreachable!(), |
765 | } |
766 | } |
767 | } |
768 | Ok(()) |
769 | } |
770 | |
771 | fn declare_handle( |
772 | &mut self, |
773 | resolve: &Resolve, |
774 | name: Option<&str>, |
775 | handle: &Handle, |
776 | ) -> Result<()> { |
777 | match name { |
778 | Some(name) => { |
779 | self.output.keyword("type" ); |
780 | self.output.str(" " ); |
781 | self.print_name_type(name, TypeKind::Resource); |
782 | self.output.str(" = " ); |
783 | // Note that the `true` here forces owned handles to be printed |
784 | // as `own<T>`. The purpose of this is because `type a = b`, if |
785 | // `b` is a resource, is encoded differently as `type a = |
786 | // own<b>`. By forcing a handle to be printed here it's staying |
787 | // true to what's in the WIT document. |
788 | self.print_handle_type(resolve, handle, true)?; |
789 | self.output.semicolon(); |
790 | |
791 | Ok(()) |
792 | } |
793 | None => bail!("document has unnamed handle type" ), |
794 | } |
795 | } |
796 | |
797 | fn declare_record( |
798 | &mut self, |
799 | resolve: &Resolve, |
800 | name: Option<&str>, |
801 | record: &Record, |
802 | ) -> Result<()> { |
803 | match name { |
804 | Some(name) => { |
805 | self.output.keyword("record" ); |
806 | self.output.str(" " ); |
807 | self.print_name_type(name, TypeKind::Record); |
808 | self.output.indent_start(); |
809 | for field in &record.fields { |
810 | self.print_docs(&field.docs); |
811 | self.print_name_param(&field.name); |
812 | self.output.str(": " ); |
813 | self.print_type_name(resolve, &field.ty)?; |
814 | self.output.str("," ); |
815 | self.output.newline(); |
816 | } |
817 | self.output.indent_end(); |
818 | Ok(()) |
819 | } |
820 | None => bail!("document has unnamed record type" ), |
821 | } |
822 | } |
823 | |
824 | fn declare_tuple( |
825 | &mut self, |
826 | resolve: &Resolve, |
827 | name: Option<&str>, |
828 | tuple: &Tuple, |
829 | ) -> Result<()> { |
830 | if let Some(name) = name { |
831 | self.output.keyword("type" ); |
832 | self.output.str(" " ); |
833 | self.print_name_type(name, TypeKind::Tuple); |
834 | self.output.str(" = " ); |
835 | self.print_tuple_type(resolve, tuple)?; |
836 | self.output.semicolon(); |
837 | } |
838 | Ok(()) |
839 | } |
840 | |
841 | fn declare_flags(&mut self, name: Option<&str>, flags: &Flags) -> Result<()> { |
842 | match name { |
843 | Some(name) => { |
844 | self.output.keyword("flags" ); |
845 | self.output.str(" " ); |
846 | self.print_name_type(name, TypeKind::Flags); |
847 | self.output.indent_start(); |
848 | for flag in &flags.flags { |
849 | self.print_docs(&flag.docs); |
850 | self.print_name_case(&flag.name); |
851 | self.output.str("," ); |
852 | self.output.newline(); |
853 | } |
854 | self.output.indent_end(); |
855 | } |
856 | None => bail!("document has unnamed flags type" ), |
857 | } |
858 | Ok(()) |
859 | } |
860 | |
861 | fn declare_variant( |
862 | &mut self, |
863 | resolve: &Resolve, |
864 | name: Option<&str>, |
865 | variant: &Variant, |
866 | ) -> Result<()> { |
867 | let name = match name { |
868 | Some(name) => name, |
869 | None => bail!("document has unnamed variant type" ), |
870 | }; |
871 | self.output.keyword("variant" ); |
872 | self.output.str(" " ); |
873 | self.print_name_type(name, TypeKind::Variant); |
874 | self.output.indent_start(); |
875 | for case in &variant.cases { |
876 | self.print_docs(&case.docs); |
877 | self.print_name_case(&case.name); |
878 | if let Some(ty) = case.ty { |
879 | self.output.str("(" ); |
880 | self.print_type_name(resolve, &ty)?; |
881 | self.output.str(")" ); |
882 | } |
883 | self.output.str("," ); |
884 | self.output.newline(); |
885 | } |
886 | self.output.indent_end(); |
887 | Ok(()) |
888 | } |
889 | |
890 | fn declare_option( |
891 | &mut self, |
892 | resolve: &Resolve, |
893 | name: Option<&str>, |
894 | payload: &Type, |
895 | ) -> Result<()> { |
896 | if let Some(name) = name { |
897 | self.output.keyword("type" ); |
898 | self.output.str(" " ); |
899 | self.print_name_type(name, TypeKind::Option); |
900 | self.output.str(" = " ); |
901 | self.print_option_type(resolve, payload)?; |
902 | self.output.semicolon(); |
903 | } |
904 | Ok(()) |
905 | } |
906 | |
907 | fn declare_result( |
908 | &mut self, |
909 | resolve: &Resolve, |
910 | name: Option<&str>, |
911 | result: &Result_, |
912 | ) -> Result<()> { |
913 | if let Some(name) = name { |
914 | self.output.keyword("type" ); |
915 | self.output.str(" " ); |
916 | self.print_name_type(name, TypeKind::Result); |
917 | self.output.str(" = " ); |
918 | self.print_result_type(resolve, result)?; |
919 | self.output.semicolon(); |
920 | } |
921 | Ok(()) |
922 | } |
923 | |
924 | fn declare_enum(&mut self, name: Option<&str>, enum_: &Enum) -> Result<()> { |
925 | let name = match name { |
926 | Some(name) => name, |
927 | None => bail!("document has unnamed enum type" ), |
928 | }; |
929 | self.output.keyword("enum" ); |
930 | self.output.str(" " ); |
931 | self.print_name_type(name, TypeKind::Enum); |
932 | self.output.indent_start(); |
933 | for case in &enum_.cases { |
934 | self.print_docs(&case.docs); |
935 | self.print_name_case(&case.name); |
936 | self.output.str("," ); |
937 | self.output.newline(); |
938 | } |
939 | self.output.indent_end(); |
940 | Ok(()) |
941 | } |
942 | |
943 | fn declare_list(&mut self, resolve: &Resolve, name: Option<&str>, ty: &Type) -> Result<()> { |
944 | if let Some(name) = name { |
945 | self.output.keyword("type" ); |
946 | self.output.str(" " ); |
947 | self.print_name_type(name, TypeKind::List); |
948 | self.output.str(" = " ); |
949 | self.output.ty("list" , TypeKind::BuiltIn); |
950 | self.output.str("<" ); |
951 | self.print_type_name(resolve, ty)?; |
952 | self.output.str(">" ); |
953 | self.output.semicolon(); |
954 | return Ok(()); |
955 | } |
956 | |
957 | Ok(()) |
958 | } |
959 | |
960 | fn declare_stream(&mut self, resolve: &Resolve, name: Option<&str>, ty: &Type) -> Result<()> { |
961 | if let Some(name) = name { |
962 | self.output.keyword("type" ); |
963 | self.output.str(" " ); |
964 | self.print_name_type(name, TypeKind::Stream); |
965 | self.output.str(" = " ); |
966 | self.output.ty("stream" , TypeKind::BuiltIn); |
967 | self.output.str("<" ); |
968 | self.print_type_name(resolve, ty)?; |
969 | self.output.str(">" ); |
970 | self.output.semicolon(); |
971 | } |
972 | |
973 | Ok(()) |
974 | } |
975 | |
976 | fn declare_future( |
977 | &mut self, |
978 | resolve: &Resolve, |
979 | name: Option<&str>, |
980 | ty: Option<&Type>, |
981 | ) -> Result<()> { |
982 | if let Some(name) = name { |
983 | self.output.keyword("type" ); |
984 | self.output.str(" " ); |
985 | self.print_name_type(name, TypeKind::Future); |
986 | self.output.str(" = " ); |
987 | self.output.ty("future" , TypeKind::BuiltIn); |
988 | if let Some(ty) = ty { |
989 | self.output.str("<" ); |
990 | self.print_type_name(resolve, ty)?; |
991 | self.output.str(">" ); |
992 | } |
993 | self.output.semicolon(); |
994 | } |
995 | |
996 | Ok(()) |
997 | } |
998 | |
999 | fn declare_error_context(&mut self, name: Option<&str>) -> Result<()> { |
1000 | if let Some(name) = name { |
1001 | self.output.keyword("type" ); |
1002 | self.output.str(" " ); |
1003 | self.print_name_type(name, TypeKind::ErrorContext); |
1004 | self.output.str(" = " ); |
1005 | self.output.ty("error-context" , TypeKind::BuiltIn); |
1006 | self.output.semicolon(); |
1007 | } |
1008 | |
1009 | Ok(()) |
1010 | } |
1011 | |
1012 | fn escape_name(name: &str) -> Cow<str> { |
1013 | if is_keyword(name) { |
1014 | Cow::Owned(format!("% {name}" )) |
1015 | } else { |
1016 | Cow::Borrowed(name) |
1017 | } |
1018 | } |
1019 | |
1020 | fn print_name_type(&mut self, name: &str, kind: TypeKind) { |
1021 | self.output.ty(Self::escape_name(name).deref(), kind); |
1022 | } |
1023 | |
1024 | fn print_name_param(&mut self, name: &str) { |
1025 | self.output.param(Self::escape_name(name).deref()); |
1026 | } |
1027 | |
1028 | fn print_name_case(&mut self, name: &str) { |
1029 | self.output.case(Self::escape_name(name).deref()); |
1030 | } |
1031 | |
1032 | fn print_docs(&mut self, docs: &Docs) { |
1033 | if self.emit_docs { |
1034 | if let Some(contents) = &docs.contents { |
1035 | for line in contents.lines() { |
1036 | self.output.doc(line); |
1037 | } |
1038 | } |
1039 | } |
1040 | } |
1041 | |
1042 | fn print_stability(&mut self, stability: &Stability) { |
1043 | match stability { |
1044 | Stability::Unknown => {} |
1045 | Stability::Stable { since, deprecated } => { |
1046 | self.output.keyword("@since" ); |
1047 | self.output.str("(" ); |
1048 | self.output.keyword("version" ); |
1049 | self.output.str(" = " ); |
1050 | self.print_name_type(&since.to_string(), TypeKind::VersionAnnotation); |
1051 | self.output.str(")" ); |
1052 | self.output.newline(); |
1053 | if let Some(version) = deprecated { |
1054 | self.output.keyword("@deprecated" ); |
1055 | self.output.str("(" ); |
1056 | self.output.keyword("version" ); |
1057 | self.output.str(" = " ); |
1058 | self.print_name_type(&version.to_string(), TypeKind::VersionAnnotation); |
1059 | self.output.str(")" ); |
1060 | self.output.newline(); |
1061 | } |
1062 | } |
1063 | Stability::Unstable { |
1064 | feature, |
1065 | deprecated, |
1066 | } => { |
1067 | self.output.keyword("@unstable" ); |
1068 | self.output.str("(" ); |
1069 | self.output.keyword("feature" ); |
1070 | self.output.str(" = " ); |
1071 | self.output.str(feature); |
1072 | self.output.str(")" ); |
1073 | self.output.newline(); |
1074 | if let Some(version) = deprecated { |
1075 | self.output.keyword("@deprecated" ); |
1076 | self.output.str("(" ); |
1077 | self.output.keyword("version" ); |
1078 | self.output.str(" = " ); |
1079 | self.print_name_type(&version.to_string(), TypeKind::VersionAnnotation); |
1080 | self.output.str(")" ); |
1081 | self.output.newline(); |
1082 | } |
1083 | } |
1084 | } |
1085 | } |
1086 | } |
1087 | |
1088 | fn resource_func(f: &Function) -> Option<TypeId> { |
1089 | match f.kind { |
1090 | FunctionKind::Freestanding => None, |
1091 | FunctionKind::Method(id: Id) | FunctionKind::Constructor(id: Id) | FunctionKind::Static(id: Id) => { |
1092 | Some(id) |
1093 | } |
1094 | } |
1095 | } |
1096 | |
1097 | fn is_keyword(name: &str) -> bool { |
1098 | matches!( |
1099 | name, |
1100 | "use" |
1101 | | "type" |
1102 | | "func" |
1103 | | "u8" |
1104 | | "u16" |
1105 | | "u32" |
1106 | | "u64" |
1107 | | "s8" |
1108 | | "s16" |
1109 | | "s32" |
1110 | | "s64" |
1111 | | "f32" |
1112 | | "f64" |
1113 | | "float32" |
1114 | | "float64" |
1115 | | "char" |
1116 | | "resource" |
1117 | | "record" |
1118 | | "flags" |
1119 | | "variant" |
1120 | | "enum" |
1121 | | "bool" |
1122 | | "string" |
1123 | | "option" |
1124 | | "result" |
1125 | | "future" |
1126 | | "stream" |
1127 | | "list" |
1128 | | "own" |
1129 | | "borrow" |
1130 | | "_" |
1131 | | "as" |
1132 | | "from" |
1133 | | "static" |
1134 | | "interface" |
1135 | | "tuple" |
1136 | | "world" |
1137 | | "import" |
1138 | | "export" |
1139 | | "package" |
1140 | | "with" |
1141 | | "include" |
1142 | | "constructor" |
1143 | | "error-context" |
1144 | ) |
1145 | } |
1146 | |
1147 | /// Trait defining visitor methods driven by [`WitPrinter`](WitPrinter). |
1148 | /// |
1149 | /// Some methods in this trait have default implementations. These default |
1150 | /// implementations may rely on helper functions that are not |
1151 | /// invoked directly by `WitPrinter`. |
1152 | pub trait Output { |
1153 | /// Push a string slice into a buffer or an output. |
1154 | /// |
1155 | /// Parameter `src` can contain punctation characters, and must be escaped |
1156 | /// when outputing to languages like HTML. |
1157 | /// Helper function used exclusively by the default implementations of trait methods. |
1158 | /// This function is not called directly by `WitPrinter`. |
1159 | /// When overriding all the trait methods, users do not need to handle this function. |
1160 | fn push_str(&mut self, src: &str); |
1161 | |
1162 | /// Set the appropriate indentation. |
1163 | /// |
1164 | /// Helper function used exclusively by the default implementations of trait methods. |
1165 | /// This function is not called directly by `WitPrinter`. |
1166 | /// When overriding all the trait methods, users do not need to handle this function. |
1167 | fn indent_if_needed(&mut self) -> bool; |
1168 | |
1169 | /// Start of indentation. In WIT this represents ` {\n`. |
1170 | fn indent_start(&mut self); |
1171 | |
1172 | /// End of indentation. In WIT this represents `}\n`. |
1173 | fn indent_end(&mut self); |
1174 | |
1175 | /// This method is designed to be used only by the default methods of this trait. |
1176 | /// Called only from the default implementation functions of this trait. |
1177 | fn indent_and_print(&mut self, src: &str) { |
1178 | assert!(!src.contains(' \n' )); |
1179 | let idented = self.indent_if_needed(); |
1180 | if idented && src.starts_with(' ' ) { |
1181 | panic!("cannot add a space at the begining of a line" ); |
1182 | } |
1183 | self.push_str(src); |
1184 | } |
1185 | |
1186 | /// A newline is added. |
1187 | fn newline(&mut self); |
1188 | |
1189 | /// A keyword is added. Keywords are hardcoded strings from `[a-z]`, but can start with `@` |
1190 | /// when printing a [Feature Gate](https://github.com/WebAssembly/component-model/blob/main/design/mvp/WIT.md#feature-gates) |
1191 | fn keyword(&mut self, src: &str) { |
1192 | self.indent_and_print(src); |
1193 | } |
1194 | |
1195 | /// A type is added. |
1196 | fn ty(&mut self, src: &str, _kind: TypeKind) { |
1197 | self.indent_and_print(src); |
1198 | } |
1199 | |
1200 | /// A parameter name of a function, record or a named return is added. |
1201 | fn param(&mut self, src: &str) { |
1202 | self.indent_and_print(src); |
1203 | } |
1204 | |
1205 | /// A case belonging to a variant, enum or flags is added. |
1206 | fn case(&mut self, src: &str) { |
1207 | self.indent_and_print(src); |
1208 | } |
1209 | |
1210 | /// Generic argument section starts. In WIT this represents the `<` character. |
1211 | fn generic_args_start(&mut self) { |
1212 | assert!( |
1213 | !self.indent_if_needed(), |
1214 | "`generic_args_start` is never called after newline" |
1215 | ); |
1216 | self.push_str("<" ); |
1217 | } |
1218 | |
1219 | /// Generic argument section ends. In WIT this represents the '>' character. |
1220 | fn generic_args_end(&mut self) { |
1221 | assert!( |
1222 | !self.indent_if_needed(), |
1223 | "`generic_args_end` is never called after newline" |
1224 | ); |
1225 | self.push_str(">" ); |
1226 | } |
1227 | |
1228 | /// Called when a single documentation line is added. |
1229 | /// The `doc` parameter starts with `///` omitted, and can be an empty string. |
1230 | fn doc(&mut self, doc: &str) { |
1231 | assert!(!doc.contains(' \n' )); |
1232 | self.indent_if_needed(); |
1233 | self.push_str("///" ); |
1234 | if !doc.is_empty() { |
1235 | self.push_str(" " ); |
1236 | self.push_str(doc); |
1237 | } |
1238 | self.newline(); |
1239 | } |
1240 | |
1241 | /// A semicolon is added. |
1242 | fn semicolon(&mut self) { |
1243 | assert!( |
1244 | !self.indent_if_needed(), |
1245 | "`semicolon` is never called after newline" |
1246 | ); |
1247 | self.push_str(";" ); |
1248 | self.newline(); |
1249 | } |
1250 | |
1251 | /// Any string that does not have a specialized function is added. |
1252 | /// Parameter `src` can contain punctation characters, and must be escaped |
1253 | /// when outputing to languages like HTML. |
1254 | fn str(&mut self, src: &str) { |
1255 | self.indent_and_print(src); |
1256 | } |
1257 | } |
1258 | |
1259 | /// Represents the different kinds of types that can be encountered while |
1260 | /// visiting a WIT file. |
1261 | /// |
1262 | /// Each variant refers to the name of the respective element (e.g., function, type, or namespace), |
1263 | /// not the entire declaration. |
1264 | #[non_exhaustive ] |
1265 | #[derive (Clone, Copy, Debug)] |
1266 | pub enum TypeKind { |
1267 | /// A built-in type, such as "list" or "option". |
1268 | BuiltIn, |
1269 | /// An enumeration type name. |
1270 | Enum, |
1271 | /// An error-context type name. |
1272 | ErrorContext, |
1273 | /// A flags type name. |
1274 | Flags, |
1275 | /// A freestanding function name, not associated with any specific type or namespace. |
1276 | /// For example, "myfunc" in `myfunc: func() -> string;`. |
1277 | FunctionFreestanding, |
1278 | /// A method, associated with a resource. |
1279 | FunctionMethod, |
1280 | /// A static function, associated with a resource. |
1281 | FunctionStatic, |
1282 | /// A future type name. |
1283 | Future, |
1284 | /// An interface declaration name. |
1285 | InterfaceDeclaration, |
1286 | /// An interface name when printing a path, for example in `use`. |
1287 | InterfacePath, |
1288 | /// A list type name. |
1289 | List, |
1290 | /// A namespace declaration. |
1291 | NamespaceDeclaration, |
1292 | /// A namespace when printing a path, for example in `use`. |
1293 | NamespacePath, |
1294 | /// An option type name. |
1295 | Option, |
1296 | /// A package name declaration. |
1297 | PackageNameDeclaration, |
1298 | /// A package name when printing a path, for example in `use`. |
1299 | PackageNamePath, |
1300 | /// A record type name. |
1301 | Record, |
1302 | /// A resource type name. |
1303 | Resource, |
1304 | /// A result type name. |
1305 | Result, |
1306 | /// A stream type name. |
1307 | Stream, |
1308 | /// A tuple type name. |
1309 | Tuple, |
1310 | /// A type alias. |
1311 | TypeAlias, |
1312 | /// An imported type name. |
1313 | TypeImport, |
1314 | /// A user-defined type name. |
1315 | TypeName, |
1316 | /// A variant type name. |
1317 | Variant, |
1318 | /// A version declaration. |
1319 | VersionDeclaration, |
1320 | /// A version when printing a path, for example in `use`. |
1321 | VersionPath, |
1322 | /// A version when printing stability annotations, for example in `@since` |
1323 | VersionAnnotation, |
1324 | /// A world declaration name. |
1325 | WorldDeclaration, |
1326 | /// A fallback for types that do not fit into any other category. |
1327 | Other, |
1328 | } |
1329 | |
1330 | /// Helper structure to help maintain an indentation level when printing source, |
1331 | /// modeled after the support in `wit-bindgen-core`. Indentation is set to two spaces. |
1332 | #[derive (Default)] |
1333 | pub struct OutputToString { |
1334 | indent: usize, |
1335 | output: String, |
1336 | // set to true after newline, then to false after first item is indented. |
1337 | needs_indent: bool, |
1338 | } |
1339 | |
1340 | impl Output for OutputToString { |
1341 | fn push_str(&mut self, src: &str) { |
1342 | self.output.push_str(src); |
1343 | } |
1344 | |
1345 | fn indent_if_needed(&mut self) -> bool { |
1346 | if self.needs_indent { |
1347 | for _ in 0..self.indent { |
1348 | // Indenting by two spaces. |
1349 | self.output.push_str(" " ); |
1350 | } |
1351 | self.needs_indent = false; |
1352 | true |
1353 | } else { |
1354 | false |
1355 | } |
1356 | } |
1357 | |
1358 | fn indent_start(&mut self) { |
1359 | assert!( |
1360 | !self.needs_indent, |
1361 | "`indent_start` is never called after newline" |
1362 | ); |
1363 | self.output.push_str(" {" ); |
1364 | self.indent += 1; |
1365 | self.newline(); |
1366 | } |
1367 | |
1368 | fn indent_end(&mut self) { |
1369 | // Note that a `saturating_sub` is used here to prevent a panic |
1370 | // here in the case of invalid code being generated in debug |
1371 | // mode. It's typically easier to debug those issues through |
1372 | // looking at the source code rather than getting a panic. |
1373 | self.indent = self.indent.saturating_sub(1); |
1374 | self.indent_if_needed(); |
1375 | self.output.push('}' ); |
1376 | self.newline(); |
1377 | } |
1378 | |
1379 | fn newline(&mut self) { |
1380 | self.output.push(' \n' ); |
1381 | self.needs_indent = true; |
1382 | } |
1383 | } |
1384 | |
1385 | impl From<OutputToString> for String { |
1386 | fn from(output: OutputToString) -> String { |
1387 | output.output |
1388 | } |
1389 | } |
1390 | |
1391 | impl Display for OutputToString { |
1392 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
1393 | self.output.fmt(f) |
1394 | } |
1395 | } |
1396 | |