1 | use crate::abi::AbiVariant; |
2 | use anyhow::{bail, Context, Result}; |
3 | use id_arena::{Arena, Id}; |
4 | use indexmap::IndexMap; |
5 | use semver::Version; |
6 | use std::borrow::Cow; |
7 | use std::fmt; |
8 | use std::path::Path; |
9 | |
10 | #[cfg (feature = "decoding" )] |
11 | pub mod decoding; |
12 | #[cfg (feature = "decoding" )] |
13 | mod metadata; |
14 | #[cfg (feature = "decoding" )] |
15 | pub use metadata::PackageMetadata; |
16 | |
17 | pub mod abi; |
18 | mod ast; |
19 | use ast::lex::Span; |
20 | pub use ast::SourceMap; |
21 | pub use ast::{parse_use_path, ParsedUsePath}; |
22 | mod sizealign; |
23 | pub use sizealign::*; |
24 | mod resolve; |
25 | pub use resolve::*; |
26 | mod live; |
27 | pub use live::{LiveTypes, TypeIdVisitor}; |
28 | |
29 | #[cfg (feature = "serde" )] |
30 | use serde_derive::Serialize; |
31 | #[cfg (feature = "serde" )] |
32 | mod serde_; |
33 | #[cfg (feature = "serde" )] |
34 | use serde_::*; |
35 | |
36 | /// Checks if the given string is a legal identifier in wit. |
37 | pub fn validate_id(s: &str) -> Result<()> { |
38 | ast::validate_id(start:0, id:s)?; |
39 | Ok(()) |
40 | } |
41 | |
42 | pub type WorldId = Id<World>; |
43 | pub type InterfaceId = Id<Interface>; |
44 | pub type TypeId = Id<TypeDef>; |
45 | |
46 | /// Representation of a parsed WIT package which has not resolved external |
47 | /// dependencies yet. |
48 | /// |
49 | /// This representation has performed internal resolution of the WIT package |
50 | /// itself, ensuring that all references internally are valid and the WIT was |
51 | /// syntactically valid and such. |
52 | /// |
53 | /// The fields of this structure represent a flat list of arrays unioned from |
54 | /// all documents within the WIT package. This means, for example, that all |
55 | /// types from all documents are located in `self.types`. The fields of each |
56 | /// item can help splitting back out into packages/interfaces/etc as necessary. |
57 | /// |
58 | /// Note that an `UnresolvedPackage` cannot be queried in general about |
59 | /// information such as size or alignment as that would require resolution of |
60 | /// foreign dependencies. Translations such as to-binary additionally are not |
61 | /// supported on an `UnresolvedPackage` due to the lack of knowledge about the |
62 | /// foreign types. This is intended to be an intermediate state which can be |
63 | /// inspected by embedders, if necessary, before quickly transforming to a |
64 | /// [`Resolve`] to fully work with a WIT package. |
65 | /// |
66 | /// After an [`UnresolvedPackage`] is parsed it can be fully resolved with |
67 | /// [`Resolve::push`]. During this operation a dependency map is specified which |
68 | /// will connect the `foreign_deps` field of this structure to packages |
69 | /// previously inserted within the [`Resolve`]. Embedders are responsible for |
70 | /// performing this resolution themselves. |
71 | #[derive (Clone)] |
72 | pub struct UnresolvedPackage { |
73 | /// The namespace, name, and version information for this package. |
74 | pub name: PackageName, |
75 | |
76 | /// All worlds from all documents within this package. |
77 | /// |
78 | /// Each world lists the document that it is from. |
79 | pub worlds: Arena<World>, |
80 | |
81 | /// All interfaces from all documents within this package. |
82 | /// |
83 | /// Each interface lists the document that it is from. Interfaces are listed |
84 | /// in topological order as well so iteration through this arena will only |
85 | /// reference prior elements already visited when working with recursive |
86 | /// references. |
87 | pub interfaces: Arena<Interface>, |
88 | |
89 | /// All types from all documents within this package. |
90 | /// |
91 | /// Each type lists the interface or world that defined it, or nothing if |
92 | /// it's an anonymous type. Types are listed in this arena in topological |
93 | /// order to ensure that iteration through this arena will only reference |
94 | /// other types transitively that are already iterated over. |
95 | pub types: Arena<TypeDef>, |
96 | |
97 | /// All foreign dependencies that this package depends on. |
98 | /// |
99 | /// These foreign dependencies must be resolved to convert this unresolved |
100 | /// package into a `Resolve`. The map here is keyed by the name of the |
101 | /// foreign package that this depends on, and the sub-map is keyed by an |
102 | /// interface name followed by the identifier within `self.interfaces`. The |
103 | /// fields of `self.interfaces` describes the required types that are from |
104 | /// each foreign interface. |
105 | pub foreign_deps: IndexMap<PackageName, IndexMap<String, AstItem>>, |
106 | |
107 | /// Doc comments for this package. |
108 | pub docs: Docs, |
109 | |
110 | package_name_span: Span, |
111 | unknown_type_spans: Vec<Span>, |
112 | interface_spans: Vec<InterfaceSpan>, |
113 | world_spans: Vec<WorldSpan>, |
114 | type_spans: Vec<Span>, |
115 | foreign_dep_spans: Vec<Span>, |
116 | required_resource_types: Vec<(TypeId, Span)>, |
117 | } |
118 | |
119 | /// Tracks a set of packages, all pulled from the same group of WIT source files. |
120 | #[derive (Clone)] |
121 | pub struct UnresolvedPackageGroup { |
122 | /// The "main" package in this package group which was found at the root of |
123 | /// the WIT files. |
124 | /// |
125 | /// Note that this is required to be present in all WIT files. |
126 | pub main: UnresolvedPackage, |
127 | |
128 | /// Nested packages found while parsing `main`, if any. |
129 | pub nested: Vec<UnresolvedPackage>, |
130 | |
131 | /// A set of processed source files from which these packages have been parsed. |
132 | pub source_map: SourceMap, |
133 | } |
134 | |
135 | #[derive (Clone)] |
136 | struct WorldSpan { |
137 | span: Span, |
138 | imports: Vec<Span>, |
139 | exports: Vec<Span>, |
140 | includes: Vec<Span>, |
141 | } |
142 | |
143 | #[derive (Clone)] |
144 | struct InterfaceSpan { |
145 | span: Span, |
146 | funcs: Vec<Span>, |
147 | } |
148 | |
149 | #[derive (Debug, Copy, Clone)] |
150 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
151 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
152 | pub enum AstItem { |
153 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
154 | Interface(InterfaceId), |
155 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
156 | World(WorldId), |
157 | } |
158 | |
159 | /// A structure used to keep track of the name of a package, containing optional |
160 | /// information such as a namespace and version information. |
161 | /// |
162 | /// This is directly encoded as an "ID" in the binary component representation |
163 | /// with an interfaced tacked on as well. |
164 | #[derive (Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)] |
165 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
166 | #[cfg_attr (feature = "serde" , serde(into = "String" ))] |
167 | pub struct PackageName { |
168 | /// A namespace such as `wasi` in `wasi:foo/bar` |
169 | pub namespace: String, |
170 | /// The kebab-name of this package, which is always specified. |
171 | pub name: String, |
172 | /// Optional major/minor version information. |
173 | pub version: Option<Version>, |
174 | } |
175 | |
176 | impl From<PackageName> for String { |
177 | fn from(name: PackageName) -> String { |
178 | name.to_string() |
179 | } |
180 | } |
181 | |
182 | impl PackageName { |
183 | /// Returns the ID that this package name would assign the `interface` name |
184 | /// specified. |
185 | pub fn interface_id(&self, interface: &str) -> String { |
186 | let mut s = String::new(); |
187 | s.push_str(&format!(" {}: {}/ {interface}" , self.namespace, self.name)); |
188 | if let Some(version) = &self.version { |
189 | s.push_str(&format!("@ {version}" )); |
190 | } |
191 | s |
192 | } |
193 | |
194 | /// Determines the "semver compatible track" for the given version. |
195 | /// |
196 | /// This method implements the logic from the component model where semver |
197 | /// versions can be compatible with one another. For example versions 1.2.0 |
198 | /// and 1.2.1 would be considered both compatible with one another because |
199 | /// they're on the same semver compatible track. |
200 | /// |
201 | /// This predicate is used during |
202 | /// [`Resolve::merge_world_imports_based_on_semver`] for example to |
203 | /// determine whether two imports can be merged together. This is |
204 | /// additionally used when creating components to match up imports in |
205 | /// core wasm to imports in worlds. |
206 | pub fn version_compat_track(version: &Version) -> Version { |
207 | let mut version = version.clone(); |
208 | version.build = semver::BuildMetadata::EMPTY; |
209 | if !version.pre.is_empty() { |
210 | return version; |
211 | } |
212 | if version.major != 0 { |
213 | version.minor = 0; |
214 | version.patch = 0; |
215 | return version; |
216 | } |
217 | if version.minor != 0 { |
218 | version.patch = 0; |
219 | return version; |
220 | } |
221 | version |
222 | } |
223 | |
224 | /// Returns the string corresponding to |
225 | /// [`PackageName::version_compat_track`]. This is done to match the |
226 | /// component model's expected naming scheme of imports and exports. |
227 | pub fn version_compat_track_string(version: &Version) -> String { |
228 | let version = Self::version_compat_track(version); |
229 | if !version.pre.is_empty() { |
230 | return version.to_string(); |
231 | } |
232 | if version.major != 0 { |
233 | return format!(" {}" , version.major); |
234 | } |
235 | if version.minor != 0 { |
236 | return format!(" {}. {}" , version.major, version.minor); |
237 | } |
238 | version.to_string() |
239 | } |
240 | } |
241 | |
242 | impl fmt::Display for PackageName { |
243 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
244 | write!(f, " {}: {}" , self.namespace, self.name)?; |
245 | if let Some(version: &Version) = &self.version { |
246 | write!(f, "@ {version}" )?; |
247 | } |
248 | Ok(()) |
249 | } |
250 | } |
251 | |
252 | #[derive (Debug)] |
253 | struct Error { |
254 | span: Span, |
255 | msg: String, |
256 | highlighted: Option<String>, |
257 | } |
258 | |
259 | impl Error { |
260 | fn new(span: Span, msg: impl Into<String>) -> Error { |
261 | Error { |
262 | span, |
263 | msg: msg.into(), |
264 | highlighted: None, |
265 | } |
266 | } |
267 | } |
268 | |
269 | impl fmt::Display for Error { |
270 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
271 | self.highlighted.as_ref().unwrap_or(&self.msg).fmt(f) |
272 | } |
273 | } |
274 | |
275 | impl std::error::Error for Error {} |
276 | |
277 | #[derive (Debug)] |
278 | struct PackageNotFoundError { |
279 | span: Span, |
280 | requested: PackageName, |
281 | known: Vec<PackageName>, |
282 | highlighted: Option<String>, |
283 | } |
284 | |
285 | impl PackageNotFoundError { |
286 | pub fn new(span: Span, requested: PackageName, known: Vec<PackageName>) -> Self { |
287 | Self { |
288 | span, |
289 | requested, |
290 | known, |
291 | highlighted: None, |
292 | } |
293 | } |
294 | } |
295 | |
296 | impl fmt::Display for PackageNotFoundError { |
297 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
298 | if let Some(highlighted: &String) = &self.highlighted { |
299 | return highlighted.fmt(f); |
300 | } |
301 | if self.known.is_empty() { |
302 | write!( |
303 | f, |
304 | "package ' {}' not found. no known packages." , |
305 | self.requested |
306 | )?; |
307 | } else { |
308 | write!( |
309 | f, |
310 | "package ' {}' not found. known packages: \n" , |
311 | self.requested |
312 | )?; |
313 | for known: &PackageName in self.known.iter() { |
314 | write!(f, " {known}\n" )?; |
315 | } |
316 | } |
317 | Ok(()) |
318 | } |
319 | } |
320 | |
321 | impl std::error::Error for PackageNotFoundError {} |
322 | |
323 | impl UnresolvedPackageGroup { |
324 | /// Parses the given string as a wit document. |
325 | /// |
326 | /// The `path` argument is used for error reporting. The `contents` provided |
327 | /// are considered to be the contents of `path`. This function does not read |
328 | /// the filesystem. |
329 | pub fn parse(path: impl AsRef<Path>, contents: &str) -> Result<UnresolvedPackageGroup> { |
330 | let mut map = SourceMap::default(); |
331 | map.push(path.as_ref(), contents); |
332 | map.parse() |
333 | } |
334 | |
335 | /// Parse a WIT package at the provided path. |
336 | /// |
337 | /// The path provided is inferred whether it's a file or a directory. A file |
338 | /// is parsed with [`UnresolvedPackageGroup::parse_file`] and a directory is |
339 | /// parsed with [`UnresolvedPackageGroup::parse_dir`]. |
340 | pub fn parse_path(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
341 | let path = path.as_ref(); |
342 | if path.is_dir() { |
343 | UnresolvedPackageGroup::parse_dir(path) |
344 | } else { |
345 | UnresolvedPackageGroup::parse_file(path) |
346 | } |
347 | } |
348 | |
349 | /// Parses a WIT package from the file provided. |
350 | /// |
351 | /// The return value represents all packages found in the WIT file which |
352 | /// might be either one or multiple depending on the syntax used. |
353 | pub fn parse_file(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
354 | let path = path.as_ref(); |
355 | let contents = std::fs::read_to_string(path) |
356 | .with_context(|| format!("failed to read file {path:?}" ))?; |
357 | Self::parse(path, &contents) |
358 | } |
359 | |
360 | /// Parses a WIT package from the directory provided. |
361 | /// |
362 | /// This method will look at all files under the `path` specified. All |
363 | /// `*.wit` files are parsed and assumed to be part of the same package |
364 | /// grouping. This is useful when a WIT package is split across multiple |
365 | /// files. |
366 | pub fn parse_dir(path: impl AsRef<Path>) -> Result<UnresolvedPackageGroup> { |
367 | let path = path.as_ref(); |
368 | let mut map = SourceMap::default(); |
369 | let cx = || format!("failed to read directory {path:?}" ); |
370 | for entry in path.read_dir().with_context(&cx)? { |
371 | let entry = entry.with_context(&cx)?; |
372 | let path = entry.path(); |
373 | let ty = entry.file_type().with_context(&cx)?; |
374 | if ty.is_dir() { |
375 | continue; |
376 | } |
377 | if ty.is_symlink() { |
378 | if path.is_dir() { |
379 | continue; |
380 | } |
381 | } |
382 | let filename = match path.file_name().and_then(|s| s.to_str()) { |
383 | Some(name) => name, |
384 | None => continue, |
385 | }; |
386 | if !filename.ends_with(".wit" ) { |
387 | continue; |
388 | } |
389 | map.push_file(&path)?; |
390 | } |
391 | map.parse() |
392 | } |
393 | } |
394 | |
395 | #[derive (Debug, Clone)] |
396 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
397 | pub struct World { |
398 | /// The WIT identifier name of this world. |
399 | pub name: String, |
400 | |
401 | /// All imported items into this interface, both worlds and functions. |
402 | pub imports: IndexMap<WorldKey, WorldItem>, |
403 | |
404 | /// All exported items from this interface, both worlds and functions. |
405 | pub exports: IndexMap<WorldKey, WorldItem>, |
406 | |
407 | /// The package that owns this world. |
408 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_optional_id" ))] |
409 | pub package: Option<PackageId>, |
410 | |
411 | /// Documentation associated with this world declaration. |
412 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
413 | pub docs: Docs, |
414 | |
415 | /// Stability annotation for this world itself. |
416 | #[cfg_attr ( |
417 | feature = "serde" , |
418 | serde(skip_serializing_if = "Stability::is_unknown" ) |
419 | )] |
420 | pub stability: Stability, |
421 | |
422 | /// All the included worlds from this world. Empty if this is fully resolved |
423 | #[cfg_attr (feature = "serde" , serde(skip))] |
424 | pub includes: Vec<(Stability, WorldId)>, |
425 | |
426 | /// All the included worlds names. Empty if this is fully resolved |
427 | #[cfg_attr (feature = "serde" , serde(skip))] |
428 | pub include_names: Vec<Vec<IncludeName>>, |
429 | } |
430 | |
431 | #[derive (Debug, Clone)] |
432 | pub struct IncludeName { |
433 | /// The name of the item |
434 | pub name: String, |
435 | |
436 | /// The name to be replaced with |
437 | pub as_: String, |
438 | } |
439 | |
440 | /// The key to the import/export maps of a world. Either a kebab-name or a |
441 | /// unique interface. |
442 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
443 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
444 | #[cfg_attr (feature = "serde" , serde(into = "String" ))] |
445 | pub enum WorldKey { |
446 | /// A kebab-name. |
447 | Name(String), |
448 | /// An interface which is assigned no kebab-name. |
449 | Interface(InterfaceId), |
450 | } |
451 | |
452 | impl From<WorldKey> for String { |
453 | fn from(key: WorldKey) -> String { |
454 | match key { |
455 | WorldKey::Name(name: String) => name, |
456 | WorldKey::Interface(id: Id) => format!("interface- {}" , id.index()), |
457 | } |
458 | } |
459 | } |
460 | |
461 | impl WorldKey { |
462 | /// Asserts that this is `WorldKey::Name` and returns the name. |
463 | #[track_caller ] |
464 | pub fn unwrap_name(self) -> String { |
465 | match self { |
466 | WorldKey::Name(name: String) => name, |
467 | WorldKey::Interface(_) => panic!("expected a name, found interface" ), |
468 | } |
469 | } |
470 | } |
471 | |
472 | #[derive (Debug, Clone, PartialEq)] |
473 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
474 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
475 | pub enum WorldItem { |
476 | /// An interface is being imported or exported from a world, indicating that |
477 | /// it's a namespace of functions. |
478 | Interface { |
479 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
480 | id: InterfaceId, |
481 | #[cfg_attr ( |
482 | feature = "serde" , |
483 | serde(skip_serializing_if = "Stability::is_unknown" ) |
484 | )] |
485 | stability: Stability, |
486 | }, |
487 | |
488 | /// A function is being directly imported or exported from this world. |
489 | Function(Function), |
490 | |
491 | /// A type is being exported from this world. |
492 | /// |
493 | /// Note that types are never imported into worlds at this time. |
494 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
495 | Type(TypeId), |
496 | } |
497 | |
498 | impl WorldItem { |
499 | pub fn stability<'a>(&'a self, resolve: &'a Resolve) -> &'a Stability { |
500 | match self { |
501 | WorldItem::Interface { stability: &Stability, .. } => stability, |
502 | WorldItem::Function(f: &Function) => &f.stability, |
503 | WorldItem::Type(id: &Id) => &resolve.types[*id].stability, |
504 | } |
505 | } |
506 | } |
507 | |
508 | #[derive (Debug, Clone)] |
509 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
510 | pub struct Interface { |
511 | /// Optionally listed name of this interface. |
512 | /// |
513 | /// This is `None` for inline interfaces in worlds. |
514 | pub name: Option<String>, |
515 | |
516 | /// Exported types from this interface. |
517 | /// |
518 | /// Export names are listed within the types themselves. Note that the |
519 | /// export name here matches the name listed in the `TypeDef`. |
520 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id_map" ))] |
521 | pub types: IndexMap<String, TypeId>, |
522 | |
523 | /// Exported functions from this interface. |
524 | pub functions: IndexMap<String, Function>, |
525 | |
526 | /// Documentation associated with this interface. |
527 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
528 | pub docs: Docs, |
529 | |
530 | /// Stability attribute for this interface. |
531 | #[cfg_attr ( |
532 | feature = "serde" , |
533 | serde(skip_serializing_if = "Stability::is_unknown" ) |
534 | )] |
535 | pub stability: Stability, |
536 | |
537 | /// The package that owns this interface. |
538 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_optional_id" ))] |
539 | pub package: Option<PackageId>, |
540 | } |
541 | |
542 | #[derive (Debug, Clone, PartialEq)] |
543 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
544 | pub struct TypeDef { |
545 | pub name: Option<String>, |
546 | pub kind: TypeDefKind, |
547 | pub owner: TypeOwner, |
548 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
549 | pub docs: Docs, |
550 | /// Stability attribute for this type. |
551 | #[cfg_attr ( |
552 | feature = "serde" , |
553 | serde(skip_serializing_if = "Stability::is_unknown" ) |
554 | )] |
555 | pub stability: Stability, |
556 | } |
557 | |
558 | #[derive (Debug, Clone, PartialEq)] |
559 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
560 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
561 | pub enum TypeDefKind { |
562 | Record(Record), |
563 | Resource, |
564 | Handle(Handle), |
565 | Flags(Flags), |
566 | Tuple(Tuple), |
567 | Variant(Variant), |
568 | Enum(Enum), |
569 | Option(Type), |
570 | Result(Result_), |
571 | List(Type), |
572 | Future(Option<Type>), |
573 | Stream(Type), |
574 | ErrorContext, |
575 | Type(Type), |
576 | |
577 | /// This represents a type of unknown structure imported from a foreign |
578 | /// interface. |
579 | /// |
580 | /// This variant is only used during the creation of `UnresolvedPackage` but |
581 | /// by the time a `Resolve` is created then this will not exist. |
582 | Unknown, |
583 | } |
584 | |
585 | impl TypeDefKind { |
586 | pub fn as_str(&self) -> &'static str { |
587 | match self { |
588 | TypeDefKind::Record(_) => "record" , |
589 | TypeDefKind::Resource => "resource" , |
590 | TypeDefKind::Handle(handle: &Handle) => match handle { |
591 | Handle::Own(_) => "own" , |
592 | Handle::Borrow(_) => "borrow" , |
593 | }, |
594 | TypeDefKind::Flags(_) => "flags" , |
595 | TypeDefKind::Tuple(_) => "tuple" , |
596 | TypeDefKind::Variant(_) => "variant" , |
597 | TypeDefKind::Enum(_) => "enum" , |
598 | TypeDefKind::Option(_) => "option" , |
599 | TypeDefKind::Result(_) => "result" , |
600 | TypeDefKind::List(_) => "list" , |
601 | TypeDefKind::Future(_) => "future" , |
602 | TypeDefKind::Stream(_) => "stream" , |
603 | TypeDefKind::ErrorContext => "error-context" , |
604 | TypeDefKind::Type(_) => "type" , |
605 | TypeDefKind::Unknown => "unknown" , |
606 | } |
607 | } |
608 | } |
609 | |
610 | #[derive (Debug, Copy, Clone, PartialEq, Eq, Hash)] |
611 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
612 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
613 | pub enum TypeOwner { |
614 | /// This type was defined within a `world` block. |
615 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
616 | World(WorldId), |
617 | /// This type was defined within an `interface` block. |
618 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
619 | Interface(InterfaceId), |
620 | /// This type wasn't inherently defined anywhere, such as a `list<T>`, which |
621 | /// doesn't need an owner. |
622 | #[cfg_attr (feature = "serde" , serde(untagged, serialize_with = "serialize_none" ))] |
623 | None, |
624 | } |
625 | |
626 | #[derive (Debug, PartialEq, Eq, Hash, Copy, Clone)] |
627 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
628 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
629 | pub enum Handle { |
630 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
631 | Own(TypeId), |
632 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
633 | Borrow(TypeId), |
634 | } |
635 | |
636 | #[derive (Debug, PartialEq, Eq, Hash, Copy, Clone)] |
637 | pub enum Type { |
638 | Bool, |
639 | U8, |
640 | U16, |
641 | U32, |
642 | U64, |
643 | S8, |
644 | S16, |
645 | S32, |
646 | S64, |
647 | F32, |
648 | F64, |
649 | Char, |
650 | String, |
651 | Id(TypeId), |
652 | } |
653 | |
654 | #[derive (Debug, Copy, Clone, Eq, PartialEq)] |
655 | pub enum Int { |
656 | U8, |
657 | U16, |
658 | U32, |
659 | U64, |
660 | } |
661 | |
662 | #[derive (Debug, Clone, PartialEq)] |
663 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
664 | pub struct Record { |
665 | pub fields: Vec<Field>, |
666 | } |
667 | |
668 | #[derive (Debug, Clone, PartialEq)] |
669 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
670 | pub struct Field { |
671 | pub name: String, |
672 | #[cfg_attr (feature = "serde" , serde(rename = "type" ))] |
673 | pub ty: Type, |
674 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
675 | pub docs: Docs, |
676 | } |
677 | |
678 | #[derive (Debug, Clone, PartialEq)] |
679 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
680 | pub struct Flags { |
681 | pub flags: Vec<Flag>, |
682 | } |
683 | |
684 | #[derive (Debug, Clone, PartialEq)] |
685 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
686 | pub struct Flag { |
687 | pub name: String, |
688 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
689 | pub docs: Docs, |
690 | } |
691 | |
692 | #[derive (Debug, Clone, PartialEq)] |
693 | pub enum FlagsRepr { |
694 | U8, |
695 | U16, |
696 | U32(usize), |
697 | } |
698 | |
699 | impl Flags { |
700 | pub fn repr(&self) -> FlagsRepr { |
701 | match self.flags.len() { |
702 | 0 => FlagsRepr::U32(0), |
703 | n: usize if n <= 8 => FlagsRepr::U8, |
704 | n: usize if n <= 16 => FlagsRepr::U16, |
705 | n: usize => FlagsRepr::U32(sizealign::align_to(val:n, align:32) / 32), |
706 | } |
707 | } |
708 | } |
709 | |
710 | impl FlagsRepr { |
711 | pub fn count(&self) -> usize { |
712 | match self { |
713 | FlagsRepr::U8 => 1, |
714 | FlagsRepr::U16 => 1, |
715 | FlagsRepr::U32(n: &usize) => *n, |
716 | } |
717 | } |
718 | } |
719 | |
720 | #[derive (Debug, Clone, PartialEq)] |
721 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
722 | pub struct Tuple { |
723 | pub types: Vec<Type>, |
724 | } |
725 | |
726 | #[derive (Debug, Clone, PartialEq)] |
727 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
728 | pub struct Variant { |
729 | pub cases: Vec<Case>, |
730 | } |
731 | |
732 | #[derive (Debug, Clone, PartialEq)] |
733 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
734 | pub struct Case { |
735 | pub name: String, |
736 | #[cfg_attr (feature = "serde" , serde(rename = "type" ))] |
737 | pub ty: Option<Type>, |
738 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
739 | pub docs: Docs, |
740 | } |
741 | |
742 | impl Variant { |
743 | pub fn tag(&self) -> Int { |
744 | discriminant_type(self.cases.len()) |
745 | } |
746 | } |
747 | |
748 | #[derive (Debug, Clone, PartialEq)] |
749 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
750 | pub struct Enum { |
751 | pub cases: Vec<EnumCase>, |
752 | } |
753 | |
754 | #[derive (Debug, Clone, PartialEq)] |
755 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
756 | pub struct EnumCase { |
757 | pub name: String, |
758 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
759 | pub docs: Docs, |
760 | } |
761 | |
762 | impl Enum { |
763 | pub fn tag(&self) -> Int { |
764 | discriminant_type(self.cases.len()) |
765 | } |
766 | } |
767 | |
768 | /// This corresponds to the `discriminant_type` function in the Canonical ABI. |
769 | fn discriminant_type(num_cases: usize) -> Int { |
770 | match num_cases.checked_sub(1) { |
771 | None => Int::U8, |
772 | Some(n: usize) if n <= u8::max_value() as usize => Int::U8, |
773 | Some(n: usize) if n <= u16::max_value() as usize => Int::U16, |
774 | Some(n: usize) if n <= u32::max_value() as usize => Int::U32, |
775 | _ => panic!("too many cases to fit in a repr" ), |
776 | } |
777 | } |
778 | |
779 | #[derive (Debug, Clone, PartialEq)] |
780 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
781 | pub struct Result_ { |
782 | pub ok: Option<Type>, |
783 | pub err: Option<Type>, |
784 | } |
785 | |
786 | #[derive (Clone, Default, Debug, PartialEq, Eq)] |
787 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
788 | pub struct Docs { |
789 | pub contents: Option<String>, |
790 | } |
791 | |
792 | impl Docs { |
793 | pub fn is_empty(&self) -> bool { |
794 | self.contents.is_none() |
795 | } |
796 | } |
797 | |
798 | pub type Params = Vec<(String, Type)>; |
799 | |
800 | #[derive (Debug, Clone, PartialEq, Eq, Hash)] |
801 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
802 | #[cfg_attr (feature = "serde" , serde(untagged))] |
803 | pub enum Results { |
804 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_params" ))] |
805 | Named(Params), |
806 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_anon_result" ))] |
807 | Anon(Type), |
808 | } |
809 | |
810 | pub enum ResultsTypeIter<'a> { |
811 | Named(std::slice::Iter<'a, (String, Type)>), |
812 | Anon(std::iter::Once<&'a Type>), |
813 | } |
814 | |
815 | impl<'a> Iterator for ResultsTypeIter<'a> { |
816 | type Item = &'a Type; |
817 | |
818 | fn next(&mut self) -> Option<&'a Type> { |
819 | match self { |
820 | ResultsTypeIter::Named(ps: &mut Iter<'_, (String, Type)>) => ps.next().map(|p: &(String, Type)| &p.1), |
821 | ResultsTypeIter::Anon(ty: impl Iterator ) => ty.next(), |
822 | } |
823 | } |
824 | |
825 | fn size_hint(&self) -> (usize, Option<usize>) { |
826 | match self { |
827 | ResultsTypeIter::Named(ps: &Iter<'_, (String, Type)>) => ps.size_hint(), |
828 | ResultsTypeIter::Anon(ty: &Once<&Type>) => ty.size_hint(), |
829 | } |
830 | } |
831 | } |
832 | |
833 | impl<'a> ExactSizeIterator for ResultsTypeIter<'a> {} |
834 | |
835 | impl Results { |
836 | // For the common case of an empty results list. |
837 | pub fn empty() -> Results { |
838 | Results::Named(Vec::new()) |
839 | } |
840 | |
841 | pub fn len(&self) -> usize { |
842 | match self { |
843 | Results::Named(params) => params.len(), |
844 | Results::Anon(_) => 1, |
845 | } |
846 | } |
847 | |
848 | pub fn throws<'a>(&self, resolve: &'a Resolve) -> Option<(Option<&'a Type>, Option<&'a Type>)> { |
849 | if self.len() != 1 { |
850 | return None; |
851 | } |
852 | match self.iter_types().next().unwrap() { |
853 | Type::Id(id) => match &resolve.types[*id].kind { |
854 | TypeDefKind::Result(r) => Some((r.ok.as_ref(), r.err.as_ref())), |
855 | _ => None, |
856 | }, |
857 | _ => None, |
858 | } |
859 | } |
860 | |
861 | pub fn iter_types(&self) -> ResultsTypeIter { |
862 | match self { |
863 | Results::Named(ps) => ResultsTypeIter::Named(ps.iter()), |
864 | Results::Anon(ty) => ResultsTypeIter::Anon(std::iter::once(ty)), |
865 | } |
866 | } |
867 | } |
868 | |
869 | #[derive (Debug, Clone, PartialEq, Eq)] |
870 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
871 | pub struct Function { |
872 | pub name: String, |
873 | pub kind: FunctionKind, |
874 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_params" ))] |
875 | pub params: Params, |
876 | pub results: Results, |
877 | #[cfg_attr (feature = "serde" , serde(skip_serializing_if = "Docs::is_empty" ))] |
878 | pub docs: Docs, |
879 | /// Stability attribute for this function. |
880 | #[cfg_attr ( |
881 | feature = "serde" , |
882 | serde(skip_serializing_if = "Stability::is_unknown" ) |
883 | )] |
884 | pub stability: Stability, |
885 | } |
886 | |
887 | #[derive (Debug, Clone, PartialEq, Eq)] |
888 | #[cfg_attr (feature = "serde" , derive(Serialize))] |
889 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
890 | pub enum FunctionKind { |
891 | Freestanding, |
892 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
893 | Method(TypeId), |
894 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
895 | Static(TypeId), |
896 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_id" ))] |
897 | Constructor(TypeId), |
898 | } |
899 | |
900 | impl FunctionKind { |
901 | /// Returns the resource, if present, that this function kind refers to. |
902 | pub fn resource(&self) -> Option<TypeId> { |
903 | match self { |
904 | FunctionKind::Freestanding => None, |
905 | FunctionKind::Method(id: &Id) | FunctionKind::Static(id: &Id) | FunctionKind::Constructor(id: &Id) => { |
906 | Some(*id) |
907 | } |
908 | } |
909 | } |
910 | } |
911 | |
912 | /// Possible forms of name mangling that are supported by this crate. |
913 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash)] |
914 | pub enum Mangling { |
915 | /// The "standard" component model mangling format for 32-bit linear |
916 | /// memories. This is specified in WebAssembly/component-model#378 |
917 | Standard32, |
918 | |
919 | /// The "legacy" name mangling supported in versions 218-and-prior for this |
920 | /// crate. This is the original support for how components were created from |
921 | /// core wasm modules and this does not correspond to any standard. This is |
922 | /// preserved for now while tools transition to the new scheme. |
923 | Legacy, |
924 | } |
925 | |
926 | impl std::str::FromStr for Mangling { |
927 | type Err = anyhow::Error; |
928 | |
929 | fn from_str(s: &str) -> Result<Mangling> { |
930 | match s { |
931 | "legacy" => Ok(Mangling::Legacy), |
932 | "standard32" => Ok(Mangling::Standard32), |
933 | _ => { |
934 | bail!( |
935 | "unknown name mangling ` {s}`, \ |
936 | supported values are `legacy` or `standard32`" |
937 | ) |
938 | } |
939 | } |
940 | } |
941 | } |
942 | |
943 | /// Possible lift/lower ABI choices supported when mangling names. |
944 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash)] |
945 | pub enum LiftLowerAbi { |
946 | /// Both imports and exports will use the synchronous ABI. |
947 | Sync, |
948 | |
949 | /// Both imports and exports will use the async ABI (with a callback for |
950 | /// each export). |
951 | AsyncCallback, |
952 | |
953 | /// Both imports and exports will use the async ABI (with no callbacks for |
954 | /// exports). |
955 | AsyncStackful, |
956 | } |
957 | |
958 | impl LiftLowerAbi { |
959 | fn import_prefix(self) -> &'static str { |
960 | match self { |
961 | Self::Sync => "" , |
962 | Self::AsyncCallback | Self::AsyncStackful => "[async]" , |
963 | } |
964 | } |
965 | |
966 | /// Get the import [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
967 | pub fn import_variant(self) -> AbiVariant { |
968 | match self { |
969 | Self::Sync => AbiVariant::GuestImport, |
970 | Self::AsyncCallback | Self::AsyncStackful => AbiVariant::GuestImportAsync, |
971 | } |
972 | } |
973 | |
974 | fn export_prefix(self) -> &'static str { |
975 | match self { |
976 | Self::Sync => "" , |
977 | Self::AsyncCallback => "[async]" , |
978 | Self::AsyncStackful => "[async-stackful]" , |
979 | } |
980 | } |
981 | |
982 | /// Get the export [`AbiVariant`] corresponding to this [`LiftLowerAbi`] |
983 | pub fn export_variant(self) -> AbiVariant { |
984 | match self { |
985 | Self::Sync => AbiVariant::GuestExport, |
986 | Self::AsyncCallback => AbiVariant::GuestExportAsync, |
987 | Self::AsyncStackful => AbiVariant::GuestExportAsyncStackful, |
988 | } |
989 | } |
990 | } |
991 | |
992 | /// Combination of [`Mangling`] and [`LiftLowerAbi`]. |
993 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash)] |
994 | pub enum ManglingAndAbi { |
995 | /// See [`Mangling::Standard32`]. |
996 | /// |
997 | /// As of this writing, the standard name mangling only supports the |
998 | /// synchronous ABI. |
999 | Standard32, |
1000 | |
1001 | /// See [`Mangling::Legacy`] and [`LiftLowerAbi`]. |
1002 | Legacy(LiftLowerAbi), |
1003 | } |
1004 | |
1005 | impl ManglingAndAbi { |
1006 | /// Get the import [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1007 | pub fn import_variant(self) -> AbiVariant { |
1008 | match self { |
1009 | Self::Standard32 => AbiVariant::GuestImport, |
1010 | Self::Legacy(abi: LiftLowerAbi) => abi.import_variant(), |
1011 | } |
1012 | } |
1013 | |
1014 | /// Get the export [`AbiVariant`] corresponding to this [`ManglingAndAbi`] |
1015 | pub fn export_variant(self) -> AbiVariant { |
1016 | match self { |
1017 | Self::Standard32 => AbiVariant::GuestExport, |
1018 | Self::Legacy(abi: LiftLowerAbi) => abi.export_variant(), |
1019 | } |
1020 | } |
1021 | } |
1022 | |
1023 | impl Function { |
1024 | pub fn item_name(&self) -> &str { |
1025 | match &self.kind { |
1026 | FunctionKind::Freestanding => &self.name, |
1027 | FunctionKind::Method(_) | FunctionKind::Static(_) => { |
1028 | &self.name[self.name.find('.' ).unwrap() + 1..] |
1029 | } |
1030 | FunctionKind::Constructor(_) => "constructor" , |
1031 | } |
1032 | } |
1033 | |
1034 | /// Returns an iterator over the types used in parameters and results. |
1035 | /// |
1036 | /// Note that this iterator is not transitive, it only iterates over the |
1037 | /// direct references to types that this function has. |
1038 | pub fn parameter_and_result_types(&self) -> impl Iterator<Item = Type> + '_ { |
1039 | self.params |
1040 | .iter() |
1041 | .map(|(_, t)| *t) |
1042 | .chain(self.results.iter_types().copied()) |
1043 | } |
1044 | |
1045 | /// Gets the core export name for this function. |
1046 | pub fn standard32_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1047 | self.core_export_name(interface, Mangling::Standard32) |
1048 | } |
1049 | |
1050 | pub fn legacy_core_export_name<'a>(&'a self, interface: Option<&str>) -> Cow<'a, str> { |
1051 | self.core_export_name(interface, Mangling::Legacy) |
1052 | } |
1053 | /// Gets the core export name for this function. |
1054 | pub fn core_export_name<'a>( |
1055 | &'a self, |
1056 | interface: Option<&str>, |
1057 | mangling: Mangling, |
1058 | ) -> Cow<'a, str> { |
1059 | match interface { |
1060 | Some(interface) => match mangling { |
1061 | Mangling::Standard32 => Cow::Owned(format!("cm32p2| {interface}| {}" , self.name)), |
1062 | Mangling::Legacy => Cow::Owned(format!(" {interface}# {}" , self.name)), |
1063 | }, |
1064 | None => match mangling { |
1065 | Mangling::Standard32 => Cow::Owned(format!("cm32p2|| {}" , self.name)), |
1066 | Mangling::Legacy => Cow::Borrowed(&self.name), |
1067 | }, |
1068 | } |
1069 | } |
1070 | /// Collect any future and stream types appearing in the signature of this |
1071 | /// function by doing a depth-first search over the parameter types and then |
1072 | /// the result types. |
1073 | /// |
1074 | /// For example, given the WIT function `foo: func(x: future<future<u32>>, |
1075 | /// y: u32) -> stream<u8>`, we would return `[future<u32>, |
1076 | /// future<future<u32>>, stream<u8>]`. |
1077 | /// |
1078 | /// This may be used by binding generators to refer to specific `future` and |
1079 | /// `stream` types when importing canonical built-ins such as `stream.new`, |
1080 | /// `future.read`, etc. Using the example above, the import |
1081 | /// `[future-new-0]foo` would indicate a call to `future.new` for the type |
1082 | /// `future<u32>`. Likewise, `[future-new-1]foo` would indicate a call to |
1083 | /// `future.new` for `future<future<u32>>`, and `[stream-new-2]foo` would |
1084 | /// indicate a call to `stream.new` for `stream<u8>`. |
1085 | pub fn find_futures_and_streams(&self, resolve: &Resolve) -> Vec<TypeId> { |
1086 | let mut results = Vec::new(); |
1087 | for (_, ty) in self.params.iter() { |
1088 | find_futures_and_streams(resolve, *ty, &mut results); |
1089 | } |
1090 | for ty in self.results.iter_types() { |
1091 | find_futures_and_streams(resolve, *ty, &mut results); |
1092 | } |
1093 | results |
1094 | } |
1095 | } |
1096 | |
1097 | fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeId>) { |
1098 | let Type::Id(id) = ty else { |
1099 | return; |
1100 | }; |
1101 | |
1102 | match &resolve.types[id].kind { |
1103 | TypeDefKind::Resource |
1104 | | TypeDefKind::Handle(_) |
1105 | | TypeDefKind::Flags(_) |
1106 | | TypeDefKind::Enum(_) |
1107 | | TypeDefKind::ErrorContext => {} |
1108 | TypeDefKind::Record(r) => { |
1109 | for Field { ty, .. } in &r.fields { |
1110 | find_futures_and_streams(resolve, *ty, results); |
1111 | } |
1112 | } |
1113 | TypeDefKind::Tuple(t) => { |
1114 | for ty in &t.types { |
1115 | find_futures_and_streams(resolve, *ty, results); |
1116 | } |
1117 | } |
1118 | TypeDefKind::Variant(v) => { |
1119 | for Case { ty, .. } in &v.cases { |
1120 | if let Some(ty) = ty { |
1121 | find_futures_and_streams(resolve, *ty, results); |
1122 | } |
1123 | } |
1124 | } |
1125 | TypeDefKind::Option(ty) | TypeDefKind::List(ty) | TypeDefKind::Type(ty) => { |
1126 | find_futures_and_streams(resolve, *ty, results); |
1127 | } |
1128 | TypeDefKind::Result(r) => { |
1129 | if let Some(ty) = r.ok { |
1130 | find_futures_and_streams(resolve, ty, results); |
1131 | } |
1132 | if let Some(ty) = r.err { |
1133 | find_futures_and_streams(resolve, ty, results); |
1134 | } |
1135 | } |
1136 | TypeDefKind::Future(ty) => { |
1137 | if let Some(ty) = ty { |
1138 | find_futures_and_streams(resolve, *ty, results); |
1139 | } |
1140 | results.push(id); |
1141 | } |
1142 | TypeDefKind::Stream(ty) => { |
1143 | find_futures_and_streams(resolve, *ty, results); |
1144 | results.push(id); |
1145 | } |
1146 | TypeDefKind::Unknown => unreachable!(), |
1147 | } |
1148 | } |
1149 | |
1150 | /// Representation of the stability attributes associated with a world, |
1151 | /// interface, function, or type. |
1152 | /// |
1153 | /// This is added for WebAssembly/component-model#332 where @since and @unstable |
1154 | /// annotations were added to WIT. |
1155 | #[derive (Debug, Clone, PartialEq, Eq)] |
1156 | #[cfg_attr (feature = "serde" , derive(serde_derive::Deserialize, Serialize))] |
1157 | #[cfg_attr (feature = "serde" , serde(rename_all = "kebab-case" ))] |
1158 | pub enum Stability { |
1159 | /// `@since(version = 1.2.3)` |
1160 | /// |
1161 | /// This item is explicitly tagged with `@since` as stable since the |
1162 | /// specified version. This may optionally have a feature listed as well. |
1163 | Stable { |
1164 | #[cfg_attr (feature = "serde" , serde(serialize_with = "serialize_version" ))] |
1165 | #[cfg_attr (feature = "serde" , serde(deserialize_with = "deserialize_version" ))] |
1166 | since: Version, |
1167 | #[cfg_attr ( |
1168 | feature = "serde" , |
1169 | serde( |
1170 | skip_serializing_if = "Option::is_none" , |
1171 | default, |
1172 | serialize_with = "serialize_optional_version" , |
1173 | deserialize_with = "deserialize_optional_version" |
1174 | ) |
1175 | )] |
1176 | deprecated: Option<Version>, |
1177 | }, |
1178 | |
1179 | /// `@unstable(feature = foo)` |
1180 | /// |
1181 | /// This item is explicitly tagged `@unstable`. A feature name is listed and |
1182 | /// this item is excluded by default in `Resolve` unless explicitly enabled. |
1183 | Unstable { |
1184 | feature: String, |
1185 | #[cfg_attr ( |
1186 | feature = "serde" , |
1187 | serde( |
1188 | skip_serializing_if = "Option::is_none" , |
1189 | default, |
1190 | serialize_with = "serialize_optional_version" , |
1191 | deserialize_with = "deserialize_optional_version" |
1192 | ) |
1193 | )] |
1194 | deprecated: Option<Version>, |
1195 | }, |
1196 | |
1197 | /// This item does not have either `@since` or `@unstable`. |
1198 | Unknown, |
1199 | } |
1200 | |
1201 | impl Stability { |
1202 | /// Returns whether this is `Stability::Unknown`. |
1203 | pub fn is_unknown(&self) -> bool { |
1204 | matches!(self, Stability::Unknown) |
1205 | } |
1206 | } |
1207 | |
1208 | impl Default for Stability { |
1209 | fn default() -> Stability { |
1210 | Stability::Unknown |
1211 | } |
1212 | } |
1213 | |
1214 | #[cfg (test)] |
1215 | mod test { |
1216 | use super::*; |
1217 | |
1218 | #[test ] |
1219 | fn test_discriminant_type() { |
1220 | assert_eq!(discriminant_type(1), Int::U8); |
1221 | assert_eq!(discriminant_type(0x100), Int::U8); |
1222 | assert_eq!(discriminant_type(0x101), Int::U16); |
1223 | assert_eq!(discriminant_type(0x10000), Int::U16); |
1224 | assert_eq!(discriminant_type(0x10001), Int::U32); |
1225 | if let Ok(num_cases) = usize::try_from(0x100000000_u64) { |
1226 | assert_eq!(discriminant_type(num_cases), Int::U32); |
1227 | } |
1228 | } |
1229 | |
1230 | #[test ] |
1231 | fn test_find_futures_and_streams() { |
1232 | let mut resolve = Resolve::default(); |
1233 | let t0 = resolve.types.alloc(TypeDef { |
1234 | name: None, |
1235 | kind: TypeDefKind::Future(Some(Type::U32)), |
1236 | owner: TypeOwner::None, |
1237 | docs: Docs::default(), |
1238 | stability: Stability::Unknown, |
1239 | }); |
1240 | let t1 = resolve.types.alloc(TypeDef { |
1241 | name: None, |
1242 | kind: TypeDefKind::Future(Some(Type::Id(t0))), |
1243 | owner: TypeOwner::None, |
1244 | docs: Docs::default(), |
1245 | stability: Stability::Unknown, |
1246 | }); |
1247 | let t2 = resolve.types.alloc(TypeDef { |
1248 | name: None, |
1249 | kind: TypeDefKind::Stream(Type::U32), |
1250 | owner: TypeOwner::None, |
1251 | docs: Docs::default(), |
1252 | stability: Stability::Unknown, |
1253 | }); |
1254 | let found = Function { |
1255 | name: "foo" .into(), |
1256 | kind: FunctionKind::Freestanding, |
1257 | params: vec![("p1" .into(), Type::Id(t1)), ("p2" .into(), Type::U32)], |
1258 | results: Results::Anon(Type::Id(t2)), |
1259 | docs: Docs::default(), |
1260 | stability: Stability::Unknown, |
1261 | } |
1262 | .find_futures_and_streams(&resolve); |
1263 | assert_eq!(3, found.len()); |
1264 | assert_eq!(t0, found[0]); |
1265 | assert_eq!(t1, found[1]); |
1266 | assert_eq!(t2, found[2]); |
1267 | } |
1268 | } |
1269 | |