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
10pub struct PackageVisitor<T>(T);
15
16impl<'a, T> PackageVisitor<T>
17where
18 T: FnMut(&'a str, Option<&'a Version>, SourceSpan) -> bool,
19{
20 pub fn new(cb: T) -> Self {
25 Self(cb)
26 }
27
28 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}