Skip to main content

rib/
instance_type.rs

1use crate::parser::PackageName;
2use crate::type_parameter::InterfaceName;
3use crate::FunctionName;
4use crate::{
5    ComponentDependency, ComponentDependencyKey, Expr, FullyQualifiedResourceConstructor,
6    FunctionDictionary, FunctionType, ResourceMethodDictionary,
7};
8use std::collections::{BTreeMap, HashMap, HashSet};
9use std::sync::Arc;
10
11use std::fmt::Debug;
12use std::ops::Deref;
13
14// `InstanceType` will be the type (`InferredType`) of the variable associated with creation of an instance
15// `InstanceType` is structured to help with compilation logic better. Example: a random `instance()` call
16// is of type `Global` to begin with and as soon as method invocations becomes a real function call,
17// the type of instance becomes more and more precise.
18//
19// Please look at `InstanceCreationType`
20// for a tangible view on the fact that an instance can be either a component instance or a resource.
21#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Ord)]
22pub enum InstanceType {
23    // Component instance: one component's exports (all packages/interfaces merged in the dictionary)
24    Global {
25        worker_name: Option<Box<Expr>>,
26        /// Shared across all instance types for one compile — avoids cloning [`FunctionDictionary`] per node.
27        component: Arc<ComponentDependency>,
28    },
29
30    // Holds the resource creation and the functions in the resource
31    // that may or may not be addressed
32    Resource {
33        analysed_resource_id: u64,
34        analysed_resource_mode: u8,
35        worker_name: Option<Box<Expr>>,
36        package_name: Option<PackageName>,
37        interface_name: Option<InterfaceName>,
38        resource_constructor: String,
39        resource_args: Vec<Expr>,
40        component_dependency_key: ComponentDependencyKey,
41        resource_method_dictionary: ResourceMethodDictionary,
42    },
43}
44
45impl InstanceType {
46    pub fn narrow_to_single_component(
47        &mut self,
48        component_dependency_key: &ComponentDependencyKey,
49    ) {
50        match self {
51            InstanceType::Global { component, .. } => {
52                Arc::make_mut(component).narrow_to_component(component_dependency_key);
53            }
54            // A resource is already narrowed down to a component
55            InstanceType::Resource { .. } => {}
56        }
57    }
58
59    pub fn set_worker_name(&mut self, worker_name: Expr) {
60        match self {
61            InstanceType::Global {
62                worker_name: wn, ..
63            } => {
64                *wn = Some(Box::new(worker_name));
65            }
66            InstanceType::Resource {
67                worker_name: wn, ..
68            } => {
69                *wn = Some(Box::new(worker_name));
70            }
71        }
72    }
73
74    pub fn worker_mut(&mut self) -> Option<&mut Box<Expr>> {
75        match self {
76            InstanceType::Global { worker_name, .. } => worker_name.as_mut(),
77            InstanceType::Resource { worker_name, .. } => worker_name.as_mut(),
78        }
79    }
80
81    pub fn worker(&self) -> Option<&Expr> {
82        match self {
83            InstanceType::Global { worker_name, .. } => worker_name.as_ref().map(|v| v.deref()),
84            InstanceType::Resource { worker_name, .. } => worker_name.as_ref().map(|v| v.deref()),
85        }
86    }
87
88    pub fn get_resource_instance_type(
89        &self,
90        fully_qualified_resource_constructor: FullyQualifiedResourceConstructor,
91        resource_args: Vec<Expr>,
92        worker_name: Option<Box<Expr>>,
93        analysed_resource_id: u64,
94        analysed_resource_mode: u8,
95    ) -> Result<InstanceType, String> {
96        let interface_name = fully_qualified_resource_constructor.interface_name.clone();
97        let package_name = fully_qualified_resource_constructor.package_name.clone();
98        let resource_constructor_name = fully_qualified_resource_constructor.resource_name.clone();
99
100        let dependencies = self.component_dependency();
101        let mut resource_method_dict = BTreeMap::new();
102
103        for (name, typ) in dependencies.function_dictionary.name_and_types.iter() {
104            if let FunctionName::ResourceMethod(resource_method) = name {
105                if resource_method.resource_name == resource_constructor_name
106                    && resource_method.interface_name == interface_name
107                    && resource_method.package_name == package_name
108                {
109                    resource_method_dict.insert(resource_method.clone(), typ.clone());
110                }
111            }
112        }
113
114        let component_dependency_key = dependencies.key.clone();
115        let resource_methods = resource_method_dict;
116
117        if !resource_methods.is_empty() {
118            let resource_method_dictionary = ResourceMethodDictionary {
119                map: resource_methods,
120            };
121
122            Ok(InstanceType::Resource {
123                worker_name,
124                package_name,
125                interface_name,
126                resource_constructor: resource_constructor_name,
127                resource_args,
128                component_dependency_key,
129                resource_method_dictionary,
130                analysed_resource_id,
131                analysed_resource_mode,
132            })
133        } else {
134            Err(format!(
135                "No components found have the resource constructor '{resource_constructor_name}'"
136            ))
137        }
138    }
139
140    pub fn interface_name(&self) -> Option<InterfaceName> {
141        match self {
142            InstanceType::Global { .. } => None,
143            InstanceType::Resource { interface_name, .. } => interface_name.clone(),
144        }
145    }
146
147    pub fn package_name(&self) -> Option<PackageName> {
148        match self {
149            InstanceType::Global { .. } => None,
150            InstanceType::Resource { package_name, .. } => package_name.clone(),
151        }
152    }
153
154    pub fn worker_name(&self) -> Option<Box<Expr>> {
155        match self {
156            InstanceType::Global { worker_name, .. } => worker_name.clone(),
157            InstanceType::Resource { worker_name, .. } => worker_name.clone(),
158        }
159    }
160
161    pub fn get_function(
162        &self,
163        method_name: &str,
164    ) -> Result<(ComponentDependencyKey, Function), String> {
165        search_function_in_instance(self, method_name)
166    }
167
168    // A flattened list of all resource methods
169    pub fn resource_method_dictionary(&self) -> FunctionDictionary {
170        let name_and_types = self
171            .component_dependency()
172            .function_dictionary
173            .name_and_types
174            .iter()
175            .filter(|(f, _)| matches!(f, FunctionName::ResourceMethod(_)))
176            .map(|(f, t)| (f.clone(), t.clone()))
177            .collect();
178
179        FunctionDictionary { name_and_types }
180    }
181
182    pub fn function_dict_without_resource_methods(&self) -> FunctionDictionary {
183        let name_and_types = self
184            .component_dependency()
185            .function_dictionary
186            .name_and_types
187            .iter()
188            .filter(|(f, _)| {
189                !matches!(f, FunctionName::ResourceMethod(_))
190                    && !matches!(f, FunctionName::Variant(_))
191                    && !matches!(f, FunctionName::Enum(_))
192            })
193            .map(|(f, t)| (f.clone(), t.clone()))
194            .collect();
195
196        FunctionDictionary { name_and_types }
197    }
198
199    pub fn component_dependency(&self) -> ComponentDependency {
200        match self {
201            InstanceType::Global { component, .. } => (**component).clone(),
202            InstanceType::Resource {
203                resource_method_dictionary,
204                component_dependency_key,
205                ..
206            } => {
207                let function_dictionary = FunctionDictionary::from(resource_method_dictionary);
208
209                ComponentDependency {
210                    key: component_dependency_key.clone(),
211                    function_dictionary,
212                }
213            }
214        }
215    }
216
217    pub fn from(
218        dependency: Arc<ComponentDependency>,
219        worker_name: Option<&Expr>,
220    ) -> Result<InstanceType, String> {
221        Ok(InstanceType::Global {
222            worker_name: worker_name.cloned().map(Box::new),
223            component: dependency,
224        })
225    }
226}
227
228#[derive(Debug, Clone)]
229pub struct Function {
230    pub function_name: FunctionName,
231    pub function_type: FunctionType,
232}
233
234fn search_function_in_instance(
235    instance: &InstanceType,
236    function_name: &str,
237) -> Result<(ComponentDependencyKey, Function), String> {
238    let dependencies = instance.component_dependency();
239    let function_dictionary = &dependencies.function_dictionary;
240    let key = dependencies.key.clone();
241
242    let functions: Vec<&(FunctionName, FunctionType)> = function_dictionary
243        .name_and_types
244        .iter()
245        .filter(|(f, _)| f.name() == function_name)
246        .collect();
247
248    if functions.is_empty() {
249        return Err(format!("function '{function_name}' not found"));
250    }
251
252    let mut package_map: HashMap<Option<PackageName>, HashSet<Option<InterfaceName>>> =
253        HashMap::new();
254
255    for (fqfn, _) in &functions {
256        package_map
257            .entry(fqfn.package_name())
258            .or_default()
259            .insert(fqfn.interface_name());
260    }
261
262    match package_map.len() {
263        1 => {
264            let interfaces = package_map.values().flatten().cloned().collect();
265            let function = search_function_in_single_package(interfaces, functions, function_name)?;
266            Ok((key, function))
267        }
268        _ => {
269            let function = search_function_in_multiple_packages(function_name, package_map)?;
270            Ok((key, function))
271        }
272    }
273}
274
275fn search_function_in_single_package(
276    interfaces: HashSet<Option<InterfaceName>>,
277    functions: Vec<&(FunctionName, FunctionType)>,
278    function_name: &str,
279) -> Result<Function, String> {
280    if interfaces.len() == 1 {
281        let (fqfn, ftype) = &functions[0];
282        Ok(Function {
283            function_name: fqfn.clone(),
284            function_type: ftype.clone(),
285        })
286    } else {
287        let mut interfaces = interfaces
288            .into_iter()
289            .filter_map(|iface| iface.map(|i| i.name))
290            .collect::<Vec<_>>();
291
292        interfaces.sort();
293
294        Err(format!(
295            "multiple interfaces contain function '{function_name}'. Rib does not currently support disambiguating instance method names across interfaces. interfaces: {}",
296            interfaces.join(", ")
297        ))
298    }
299}
300
301fn search_function_in_multiple_packages(
302    function_name: &str,
303    package_map: HashMap<Option<PackageName>, HashSet<Option<InterfaceName>>>,
304) -> Result<Function, String> {
305    let mut error_msg = format!(
306        "function '{function_name}' exists in multiple packages. Rib does not currently support disambiguating instance method names in this case. Conflicting exports: "
307    );
308
309    let mut package_interface_list = package_map
310        .into_iter()
311        .filter_map(|(pkg, interfaces)| {
312            pkg.map(|p| {
313                let mut interface_list = interfaces
314                    .into_iter()
315                    .filter_map(|iface| iface.map(|i| i.name))
316                    .collect::<Vec<_>>();
317
318                interface_list.sort();
319
320                if interface_list.is_empty() {
321                    format!("{p}")
322                } else {
323                    format!("{} (interfaces: {})", p, interface_list.join(", "))
324                }
325            })
326        })
327        .collect::<Vec<_>>();
328
329    package_interface_list.sort();
330
331    error_msg.push_str(&package_interface_list.join(", "));
332    Err(error_msg)
333}