1use crate::parser::PackageName;
16use crate::type_parameter::InterfaceName;
17use crate::FunctionName;
18use crate::{
19 ComponentDependency, ComponentDependencyKey, Expr, FullyQualifiedResourceConstructor,
20 FunctionDictionary, FunctionType, ResourceMethodDictionary,
21};
22use std::collections::{BTreeMap, HashMap, HashSet};
23use std::sync::Arc;
24
25use std::fmt::Debug;
26use std::ops::Deref;
27
28#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Ord)]
36pub enum InstanceType {
37 Global {
39 worker_name: Option<Box<Expr>>,
40 component: Arc<ComponentDependency>,
42 },
43
44 Resource {
47 analysed_resource_id: u64,
48 analysed_resource_mode: u8,
49 worker_name: Option<Box<Expr>>,
50 package_name: Option<PackageName>,
51 interface_name: Option<InterfaceName>,
52 resource_constructor: String,
53 resource_args: Vec<Expr>,
54 component_dependency_key: ComponentDependencyKey,
55 resource_method_dictionary: ResourceMethodDictionary,
56 },
57}
58
59impl InstanceType {
60 pub fn narrow_to_single_component(
61 &mut self,
62 component_dependency_key: &ComponentDependencyKey,
63 ) {
64 match self {
65 InstanceType::Global { component, .. } => {
66 Arc::make_mut(component).narrow_to_component(component_dependency_key);
67 }
68 InstanceType::Resource { .. } => {}
70 }
71 }
72
73 pub fn set_worker_name(&mut self, worker_name: Expr) {
74 match self {
75 InstanceType::Global {
76 worker_name: wn, ..
77 } => {
78 *wn = Some(Box::new(worker_name));
79 }
80 InstanceType::Resource {
81 worker_name: wn, ..
82 } => {
83 *wn = Some(Box::new(worker_name));
84 }
85 }
86 }
87
88 pub fn worker_mut(&mut self) -> Option<&mut Box<Expr>> {
89 match self {
90 InstanceType::Global { worker_name, .. } => worker_name.as_mut(),
91 InstanceType::Resource { worker_name, .. } => worker_name.as_mut(),
92 }
93 }
94
95 pub fn worker(&self) -> Option<&Expr> {
96 match self {
97 InstanceType::Global { worker_name, .. } => worker_name.as_ref().map(|v| v.deref()),
98 InstanceType::Resource { worker_name, .. } => worker_name.as_ref().map(|v| v.deref()),
99 }
100 }
101
102 pub fn get_resource_instance_type(
103 &self,
104 fully_qualified_resource_constructor: FullyQualifiedResourceConstructor,
105 resource_args: Vec<Expr>,
106 worker_name: Option<Box<Expr>>,
107 analysed_resource_id: u64,
108 analysed_resource_mode: u8,
109 ) -> Result<InstanceType, String> {
110 let interface_name = fully_qualified_resource_constructor.interface_name.clone();
111 let package_name = fully_qualified_resource_constructor.package_name.clone();
112 let resource_constructor_name = fully_qualified_resource_constructor.resource_name.clone();
113
114 let dependencies = self.component_dependency();
115 let mut resource_method_dict = BTreeMap::new();
116
117 for (name, typ) in dependencies.function_dictionary.name_and_types.iter() {
118 if let FunctionName::ResourceMethod(resource_method) = name {
119 if resource_method.resource_name == resource_constructor_name
120 && resource_method.interface_name == interface_name
121 && resource_method.package_name == package_name
122 {
123 resource_method_dict.insert(resource_method.clone(), typ.clone());
124 }
125 }
126 }
127
128 let component_dependency_key = dependencies.key.clone();
129 let resource_methods = resource_method_dict;
130
131 if !resource_methods.is_empty() {
132 let resource_method_dictionary = ResourceMethodDictionary {
133 map: resource_methods,
134 };
135
136 Ok(InstanceType::Resource {
137 worker_name,
138 package_name,
139 interface_name,
140 resource_constructor: resource_constructor_name,
141 resource_args,
142 component_dependency_key,
143 resource_method_dictionary,
144 analysed_resource_id,
145 analysed_resource_mode,
146 })
147 } else {
148 Err(format!(
149 "No components found have the resource constructor '{resource_constructor_name}'"
150 ))
151 }
152 }
153
154 pub fn interface_name(&self) -> Option<InterfaceName> {
155 match self {
156 InstanceType::Global { .. } => None,
157 InstanceType::Resource { interface_name, .. } => interface_name.clone(),
158 }
159 }
160
161 pub fn package_name(&self) -> Option<PackageName> {
162 match self {
163 InstanceType::Global { .. } => None,
164 InstanceType::Resource { package_name, .. } => package_name.clone(),
165 }
166 }
167
168 pub fn worker_name(&self) -> Option<Box<Expr>> {
169 match self {
170 InstanceType::Global { worker_name, .. } => worker_name.clone(),
171 InstanceType::Resource { worker_name, .. } => worker_name.clone(),
172 }
173 }
174
175 pub fn get_function(
176 &self,
177 method_name: &str,
178 ) -> Result<(ComponentDependencyKey, Function), String> {
179 search_function_in_instance(self, method_name)
180 }
181
182 pub fn resource_method_dictionary(&self) -> FunctionDictionary {
184 let name_and_types = self
185 .component_dependency()
186 .function_dictionary
187 .name_and_types
188 .iter()
189 .filter(|(f, _)| matches!(f, FunctionName::ResourceMethod(_)))
190 .map(|(f, t)| (f.clone(), t.clone()))
191 .collect();
192
193 FunctionDictionary { name_and_types }
194 }
195
196 pub fn function_dict_without_resource_methods(&self) -> FunctionDictionary {
197 let name_and_types = self
198 .component_dependency()
199 .function_dictionary
200 .name_and_types
201 .iter()
202 .filter(|(f, _)| {
203 !matches!(f, FunctionName::ResourceMethod(_))
204 && !matches!(f, FunctionName::Variant(_))
205 && !matches!(f, FunctionName::Enum(_))
206 })
207 .map(|(f, t)| (f.clone(), t.clone()))
208 .collect();
209
210 FunctionDictionary { name_and_types }
211 }
212
213 pub fn component_dependency(&self) -> ComponentDependency {
214 match self {
215 InstanceType::Global { component, .. } => (**component).clone(),
216 InstanceType::Resource {
217 resource_method_dictionary,
218 component_dependency_key,
219 ..
220 } => {
221 let function_dictionary = FunctionDictionary::from(resource_method_dictionary);
222
223 ComponentDependency {
224 key: component_dependency_key.clone(),
225 function_dictionary,
226 }
227 }
228 }
229 }
230
231 pub fn from(
232 dependency: Arc<ComponentDependency>,
233 worker_name: Option<&Expr>,
234 ) -> Result<InstanceType, String> {
235 Ok(InstanceType::Global {
236 worker_name: worker_name.cloned().map(Box::new),
237 component: dependency,
238 })
239 }
240}
241
242#[derive(Debug, Clone)]
243pub struct Function {
244 pub function_name: FunctionName,
245 pub function_type: FunctionType,
246}
247
248fn search_function_in_instance(
249 instance: &InstanceType,
250 function_name: &str,
251) -> Result<(ComponentDependencyKey, Function), String> {
252 let dependencies = instance.component_dependency();
253 let function_dictionary = &dependencies.function_dictionary;
254 let key = dependencies.key.clone();
255
256 let functions: Vec<&(FunctionName, FunctionType)> = function_dictionary
257 .name_and_types
258 .iter()
259 .filter(|(f, _)| f.name() == function_name)
260 .collect();
261
262 if functions.is_empty() {
263 return Err(format!("function '{function_name}' not found"));
264 }
265
266 let mut package_map: HashMap<Option<PackageName>, HashSet<Option<InterfaceName>>> =
267 HashMap::new();
268
269 for (fqfn, _) in &functions {
270 package_map
271 .entry(fqfn.package_name())
272 .or_default()
273 .insert(fqfn.interface_name());
274 }
275
276 match package_map.len() {
277 1 => {
278 let interfaces = package_map.values().flatten().cloned().collect();
279 let function = search_function_in_single_package(interfaces, functions, function_name)?;
280 Ok((key, function))
281 }
282 _ => {
283 let function = search_function_in_multiple_packages(function_name, package_map)?;
284 Ok((key, function))
285 }
286 }
287}
288
289fn search_function_in_single_package(
290 interfaces: HashSet<Option<InterfaceName>>,
291 functions: Vec<&(FunctionName, FunctionType)>,
292 function_name: &str,
293) -> Result<Function, String> {
294 if interfaces.len() == 1 {
295 let (fqfn, ftype) = &functions[0];
296 Ok(Function {
297 function_name: fqfn.clone(),
298 function_type: ftype.clone(),
299 })
300 } else {
301 let mut interfaces = interfaces
302 .into_iter()
303 .filter_map(|iface| iface.map(|i| i.name))
304 .collect::<Vec<_>>();
305
306 interfaces.sort();
307
308 Err(format!(
309 "multiple interfaces contain function '{function_name}'; disambiguate with a fully qualified WIT call (e.g. ns:pkg/interface.{{fn}}). interfaces: {}",
310 interfaces.join(", ")
311 ))
312 }
313}
314
315fn search_function_in_multiple_packages(
316 function_name: &str,
317 package_map: HashMap<Option<PackageName>, HashSet<Option<InterfaceName>>>,
318) -> Result<Function, String> {
319 let mut error_msg = format!(
320 "function '{function_name}' exists in multiple packages; use a fully qualified WIT call site to disambiguate: "
321 );
322
323 let mut package_interface_list = package_map
324 .into_iter()
325 .filter_map(|(pkg, interfaces)| {
326 pkg.map(|p| {
327 let mut interface_list = interfaces
328 .into_iter()
329 .filter_map(|iface| iface.map(|i| i.name))
330 .collect::<Vec<_>>();
331
332 interface_list.sort();
333
334 if interface_list.is_empty() {
335 format!("{p}")
336 } else {
337 format!("{} (interfaces: {})", p, interface_list.join(", "))
338 }
339 })
340 })
341 .collect::<Vec<_>>();
342
343 package_interface_list.sort();
344
345 error_msg.push_str(&package_interface_list.join(", "));
346 Err(error_msg)
347}