rib/registry/
component_dependencies.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::{
16    ComponentDependencyKey, Expr, FullyQualifiedInterfaceName, FunctionDictionary, FunctionName,
17    FunctionType, FunctionTypeRegistry, InstanceCreationType, InterfaceName, PackageName,
18    TypeParameter,
19};
20use golem_wasm_ast::analysis::TypeEnum;
21use golem_wasm_ast::analysis::{AnalysedExport, TypeVariant};
22use std::collections::BTreeMap;
23
24#[derive(Debug, Default, Hash, Clone, Eq, PartialEq, PartialOrd, Ord)]
25pub struct ComponentDependencies {
26    pub dependencies: BTreeMap<ComponentDependencyKey, FunctionDictionary>,
27}
28
29impl ComponentDependencies {
30    pub fn size(&self) -> usize {
31        self.dependencies.len()
32    }
33
34    pub fn get_variants(&self) -> Vec<TypeVariant> {
35        let mut variants = vec![];
36
37        for function_dict in self.dependencies.values() {
38            variants.extend(function_dict.get_all_variants());
39        }
40
41        variants
42    }
43
44    pub fn get_enums(&self) -> Vec<TypeEnum> {
45        let mut enums = vec![];
46
47        for function_dict in self.dependencies.values() {
48            enums.extend(function_dict.get_all_enums());
49        }
50
51        enums
52    }
53
54    pub fn get_function_type(
55        &self,
56        component_info: &Option<ComponentDependencyKey>,
57        function_name: &FunctionName,
58    ) -> Result<FunctionType, String> {
59        // If function name is unique across all components, we are not in need of a component_info per se
60        // and we can return the exact component dependency
61        match component_info {
62            None => {
63                let mut function_types_in_component = vec![];
64
65                for (component_info, function_dict) in &self.dependencies {
66                    let types = function_dict
67                        .name_and_types
68                        .iter()
69                        .filter_map(|(f_name, function_type)| {
70                            if f_name == function_name {
71                                Some(function_type)
72                            } else {
73                                None
74                            }
75                        })
76                        .collect::<Vec<_>>();
77
78                    function_types_in_component.push((component_info.clone(), types));
79                }
80
81                if function_types_in_component.is_empty() {
82                    Err("unknown function".to_string())
83                } else if function_types_in_component.len() > 1 {
84                    Err(format!(
85                        "function `{}` is ambiguous across components",
86                        function_name
87                    ))
88                } else {
89                    let function_types = function_types_in_component.pop().unwrap();
90                    let function_type = function_types.1;
91
92                    if function_type.is_empty() {
93                        Err("unknown function".to_string())
94                    } else {
95                        Ok(function_type[0].clone())
96                    }
97                }
98            }
99            Some(component_info) => {
100                let function_dictionary = self
101                    .dependencies
102                    .get(component_info)
103                    .cloned()
104                    .ok_or_else(|| {
105                        format!(
106                            "component dependency for `{}` not found",
107                            component_info.component_name
108                        )
109                    })?;
110
111                let function_type = function_dictionary.name_and_types.iter().find_map(
112                    |(f_name, function_type)| {
113                        if f_name == function_name {
114                            Some(function_type.clone())
115                        } else {
116                            None
117                        }
118                    },
119                );
120
121                if let Some(function_type) = function_type {
122                    Ok(function_type)
123                } else {
124                    Err(format!(
125                        "function `{}` not found in component `{}`",
126                        function_name, component_info.component_name
127                    ))
128                }
129            }
130        }
131    }
132
133    pub fn narrow_to_component(&mut self, component_dependency_key: &ComponentDependencyKey) {
134        // If the component dependency key is not found, we do nothing
135        if let Some(function_dict) = self.dependencies.remove(component_dependency_key) {
136            self.dependencies.clear();
137            self.dependencies
138                .insert(component_dependency_key.clone(), function_dict);
139        }
140    }
141
142    pub fn function_dictionary(&self) -> Vec<&FunctionDictionary> {
143        self.dependencies.values().collect::<Vec<_>>()
144    }
145
146    pub fn filter_by_interface(
147        &self,
148        interface_name: &InterfaceName,
149    ) -> Result<crate::ComponentDependencies, String> {
150        let mut tree = BTreeMap::new();
151
152        for (component_info, function_dict) in self.dependencies.iter() {
153            let name_and_types: Vec<&(FunctionName, FunctionType)> = function_dict
154                .name_and_types
155                .iter()
156                .filter(|(f, _)| f.interface_name().as_ref() == Some(interface_name))
157                .collect::<Vec<_>>();
158
159            if !name_and_types.is_empty() {
160                tree.insert(
161                    component_info.clone(),
162                    FunctionDictionary {
163                        name_and_types: name_and_types.into_iter().cloned().collect(),
164                    },
165                );
166            }
167        }
168
169        if tree.is_empty() {
170            return Err(format!("interface `{}` not found", interface_name));
171        }
172
173        Ok(ComponentDependencies { dependencies: tree })
174    }
175
176    pub fn filter_by_package_name(
177        &self,
178        package_name: &PackageName,
179    ) -> Result<crate::ComponentDependencies, String> {
180        // If the package name corresponds to the root package name we pick that up
181        let mut tree = BTreeMap::new();
182
183        for (component_info, function_dict) in self.dependencies.iter() {
184            if let Some(root_package_name) = &component_info.root_package_name {
185                if root_package_name == &package_name.to_string() {
186                    tree.insert(component_info.clone(), function_dict.clone());
187                }
188            } else {
189                // If this package doesn't correspond to a root, but happens to be part of the component then
190
191                let name_and_types = function_dict
192                    .name_and_types
193                    .iter()
194                    .filter(|(f, _)| f.package_name() == Some(package_name.clone()))
195                    .collect::<Vec<_>>();
196
197                if !name_and_types.is_empty() {
198                    tree.insert(
199                        component_info.clone(),
200                        FunctionDictionary {
201                            name_and_types: name_and_types.into_iter().cloned().collect(),
202                        },
203                    );
204                }
205            }
206        }
207
208        if tree.is_empty() {
209            return Err(format!("package `{}` not found", package_name));
210        }
211
212        Ok(crate::ComponentDependencies { dependencies: tree })
213    }
214
215    pub fn filter_by_fully_qualified_interface(
216        &self,
217        fqi: &FullyQualifiedInterfaceName,
218    ) -> Result<Self, String> {
219        let mut tree = BTreeMap::new();
220
221        for (component_info, function_dict) in self.dependencies.iter() {
222            if let Some(root_package_name) = &component_info.root_package_name {
223                if root_package_name == &fqi.package_name.to_string() {
224                    tree.insert(component_info.clone(), function_dict.clone());
225                }
226            } else {
227                // If this package doesn't correspond to a root, but happens to be part of the component then
228
229                let name_and_types = function_dict
230                    .name_and_types
231                    .iter()
232                    .filter(|(f, _)| {
233                        f.package_name() == Some(fqi.package_name.clone())
234                            && f.interface_name() == Some(fqi.interface_name.clone())
235                    })
236                    .collect::<Vec<_>>();
237
238                if !name_and_types.is_empty() {
239                    tree.insert(
240                        component_info.clone(),
241                        FunctionDictionary {
242                            name_and_types: name_and_types.into_iter().cloned().collect(),
243                        },
244                    );
245                }
246            }
247        }
248
249        if tree.is_empty() {
250            return Err(format!("`{}` not found", fqi));
251        }
252
253        Ok(ComponentDependencies { dependencies: tree })
254    }
255
256    // type-parameter can be None.
257    // If present, it may represent the root package name of the component
258    // or it could represent the package or interface within a component
259    pub fn get_worker_instance_type(
260        &self,
261        type_parameter: Option<TypeParameter>,
262        worker_name: Option<Expr>,
263    ) -> Result<InstanceCreationType, String> {
264        match type_parameter {
265            None => Ok(InstanceCreationType::WitWorker {
266                component_info: None,
267                worker_name: worker_name.map(Box::new),
268            }),
269
270            Some(TypeParameter::PackageName(package_name)) => {
271                // If the user has specified the root package name, annotate the InstanceCreationType with the component already
272                let result = self
273                    .dependencies
274                    .iter()
275                    .find(|(x, _)| match &x.root_package_name {
276                        Some(name) => {
277                            let pkg = match &x.root_package_version {
278                                None => name.to_string(),
279                                Some(version) => format!("{}@{}", name, version),
280                            };
281
282                            pkg == package_name.to_string()
283                        }
284
285                        None => false,
286                    });
287
288                if let Some(result) = result {
289                    Ok(InstanceCreationType::WitWorker {
290                        component_info: Some(result.0.clone()),
291                        worker_name: worker_name.map(Box::new),
292                    })
293                } else {
294                    Ok(InstanceCreationType::WitWorker {
295                        component_info: None,
296                        worker_name: worker_name.map(Box::new),
297                    })
298                }
299            }
300
301            _ => Ok(InstanceCreationType::WitWorker {
302                component_info: None,
303                worker_name: worker_name.map(Box::new),
304            }),
305        }
306    }
307
308    pub fn from_raw(
309        component_and_exports: Vec<(ComponentDependencyKey, &Vec<AnalysedExport>)>,
310    ) -> Result<Self, String> {
311        let mut dependencies = BTreeMap::new();
312
313        for (component_info, exports) in component_and_exports {
314            let function_type_registry = FunctionTypeRegistry::from_export_metadata(exports);
315            let function_dictionary =
316                FunctionDictionary::from_function_type_registry(&function_type_registry)?;
317            dependencies.insert(component_info, function_dictionary);
318        }
319
320        Ok(ComponentDependencies { dependencies })
321    }
322}