Skip to main content

rib/
instance_type.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Golem Source License v1.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://license.golem.cloud/LICENSE
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use 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// `InstanceType` will be the type (`InferredType`) of the variable associated with creation of an instance
29// `InstanceType` is structured to help with compilation logic better. Example: a random `instance()` call
30// is of type `Global` to begin with and as soon as method invocations becomes a real function call,
31// the type of instance becomes more and more precise.
32//
33// Please look at `InstanceCreationType`
34// for a tangible view on the fact that an instance can be either worker or a resource.
35#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Ord)]
36pub enum InstanceType {
37    // Worker instance: one component's exports (all packages/interfaces merged in the dictionary)
38    Global {
39        worker_name: Option<Box<Expr>>,
40        /// Shared across all worker-instance types for one compile — avoids cloning [`FunctionDictionary`] per node.
41        component: Arc<ComponentDependency>,
42    },
43
44    // Holds the resource creation and the functions in the resource
45    // that may or may not be addressed
46    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            // A resource is already narrowed down to a component
69            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    // A flattened list of all resource methods
183    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}