wac_resolver/
visitor.rs

1use miette::SourceSpan;
2use semver::Version;
3use wac_parser::{
4    Document, Expr, ExternType, ImportStatement, ImportType, InstantiationArgument, InterfaceItem,
5    PrimaryExpr, Statement, TypeStatement, UsePath, WorldItem, WorldItemPath, WorldRef,
6};
7
8use crate::Error;
9
10/// A visitor of packages referenced in a document.
11///
12/// This can be used to collect all of the packages referenced
13/// in a document so that they may all be resolved at the same time.
14pub struct PackageVisitor<T>(T);
15
16impl<'a, T> PackageVisitor<T>
17where
18    T: FnMut(&'a str, Option<&'a Version>, SourceSpan) -> bool,
19{
20    /// Creates a new package visitor with the given callback.
21    ///
22    /// The callback receives the package name, optional version, and
23    /// the span of the package name.
24    pub fn new(cb: T) -> Self {
25        Self(cb)
26    }
27
28    /// Visits any package names referenced in the document.
29    ///
30    /// The package names are visited in-order and will not deduplicate
31    /// names/versions that are referenced multiple times.
32    pub fn visit(&mut self, doc: &'a Document) -> Result<(), Error> {
33        if let Some(targets) = &doc.directive.targets {
34            if !(self.0)(
35                targets.name,
36                targets.version.as_ref(),
37                targets.package_name_span(),
38            ) {
39                return Ok(());
40            }
41        }
42
43        for stmt in &doc.statements {
44            match stmt {
45                Statement::Import(i) => {
46                    if !self.import_statement(i) {
47                        break;
48                    }
49                }
50                Statement::Type(t) => {
51                    if !self.type_statement(t) {
52                        break;
53                    }
54                }
55                Statement::Let(l) => {
56                    if !self.expr(doc.directive.package.name, &l.expr)? {
57                        break;
58                    }
59                }
60                Statement::Export(e) => {
61                    if !self.expr(doc.directive.package.name, &e.expr)? {
62                        break;
63                    }
64                }
65            }
66        }
67
68        Ok(())
69    }
70
71    fn import_statement(&mut self, stmt: &'a ImportStatement) -> bool {
72        match &stmt.ty {
73            ImportType::Package(p) => (self.0)(p.name, p.version.as_ref(), p.package_name_span()),
74            ImportType::Interface(i) => {
75                for item in &i.items {
76                    if !self.interface_item(item) {
77                        return false;
78                    }
79                }
80
81                true
82            }
83            ImportType::Func(_) | ImportType::Ident(_) => true,
84        }
85    }
86
87    fn type_statement(&mut self, stmt: &'a TypeStatement) -> bool {
88        match stmt {
89            TypeStatement::Interface(i) => {
90                for item in &i.items {
91                    if !self.interface_item(item) {
92                        return false;
93                    }
94                }
95
96                true
97            }
98            TypeStatement::World(w) => {
99                for item in &w.items {
100                    if !self.world_item(item) {
101                        return false;
102                    }
103                }
104
105                true
106            }
107            TypeStatement::Type(_) => true,
108        }
109    }
110
111    fn interface_item(&mut self, item: &'a InterfaceItem) -> bool {
112        match item {
113            InterfaceItem::Use(u) => match &u.path {
114                UsePath::Package(p) => (self.0)(p.name, p.version.as_ref(), p.package_name_span()),
115                UsePath::Ident(_) => true,
116            },
117            InterfaceItem::Type(_) | InterfaceItem::Export(_) => true,
118        }
119    }
120
121    fn world_item(&mut self, item: &'a WorldItem) -> bool {
122        match item {
123            WorldItem::Use(u) => match &u.path {
124                UsePath::Package(p) => (self.0)(p.name, p.version.as_ref(), p.package_name_span()),
125                UsePath::Ident(_) => true,
126            },
127            WorldItem::Type(_) => true,
128            WorldItem::Import(i) => self.world_item_path(&i.path),
129            WorldItem::Export(e) => self.world_item_path(&e.path),
130            WorldItem::Include(i) => match &i.world {
131                WorldRef::Package(p) => (self.0)(p.name, p.version.as_ref(), p.package_name_span()),
132                WorldRef::Ident(_) => true,
133            },
134        }
135    }
136
137    fn world_item_path(&mut self, path: &'a WorldItemPath) -> bool {
138        match path {
139            WorldItemPath::Named(n) => match &n.ty {
140                ExternType::Interface(i) => {
141                    for item in &i.items {
142                        if !self.interface_item(item) {
143                            return false;
144                        }
145                    }
146
147                    true
148                }
149                ExternType::Ident(_) | ExternType::Func(_) => true,
150            },
151            WorldItemPath::Package(p) => {
152                (self.0)(p.name, p.version.as_ref(), p.package_name_span())
153            }
154            WorldItemPath::Ident(_) => true,
155        }
156    }
157
158    fn expr(&mut self, this: &str, expr: &'a Expr) -> Result<bool, Error> {
159        match &expr.primary {
160            PrimaryExpr::New(e) => {
161                if e.package.name == this {
162                    return Err(Error::CannotInstantiateSelf {
163                        span: e.package.span,
164                    });
165                }
166
167                if !(self.0)(e.package.name, e.package.version.as_ref(), e.package.span) {
168                    return Ok(false);
169                }
170
171                for arg in &e.arguments {
172                    match arg {
173                        InstantiationArgument::Inferred(_)
174                        | InstantiationArgument::Spread(_)
175                        | InstantiationArgument::Fill(_) => continue,
176                        InstantiationArgument::Named(a) => {
177                            if !self.expr(this, &a.expr)? {
178                                return Ok(false);
179                            }
180                        }
181                    }
182                }
183
184                Ok(true)
185            }
186            PrimaryExpr::Nested(e) => self.expr(this, &e.inner),
187            PrimaryExpr::Ident(_) => Ok(true),
188        }
189    }
190}