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 `{function_name}` is ambiguous across components"
86                    ))
87                } else {
88                    let function_types = function_types_in_component.pop().unwrap();
89                    let function_type = function_types.1;
90
91                    if function_type.is_empty() {
92                        Err("unknown function".to_string())
93                    } else {
94                        Ok(function_type[0].clone())
95                    }
96                }
97            }
98            Some(component_info) => {
99                let function_dictionary = self
100                    .dependencies
101                    .get(component_info)
102                    .cloned()
103                    .ok_or_else(|| {
104                        format!(
105                            "component dependency for `{}` not found",
106                            component_info.component_name
107                        )
108                    })?;
109
110                let function_type = function_dictionary.name_and_types.iter().find_map(
111                    |(f_name, function_type)| {
112                        if f_name == function_name {
113                            Some(function_type.clone())
114                        } else {
115                            None
116                        }
117                    },
118                );
119
120                if let Some(function_type) = function_type {
121                    Ok(function_type)
122                } else {
123                    Err(format!(
124                        "function `{}` not found in component `{}`",
125                        function_name, component_info.component_name
126                    ))
127                }
128            }
129        }
130    }
131
132    pub fn narrow_to_component(&mut self, component_dependency_key: &ComponentDependencyKey) {
133        // If the component dependency key is not found, we do nothing
134        if let Some(function_dict) = self.dependencies.remove(component_dependency_key) {
135            self.dependencies.clear();
136            self.dependencies
137                .insert(component_dependency_key.clone(), function_dict);
138        }
139    }
140
141    pub fn function_dictionary(&self) -> Vec<&FunctionDictionary> {
142        self.dependencies.values().collect::<Vec<_>>()
143    }
144
145    pub fn filter_by_interface(
146        &self,
147        interface_name: &InterfaceName,
148    ) -> Result<crate::ComponentDependencies, String> {
149        let mut tree = BTreeMap::new();
150
151        for (component_info, function_dict) in self.dependencies.iter() {
152            let name_and_types: Vec<&(FunctionName, FunctionType)> = function_dict
153                .name_and_types
154                .iter()
155                .filter(|(f, _)| f.interface_name().as_ref() == Some(interface_name))
156                .collect::<Vec<_>>();
157
158            if !name_and_types.is_empty() {
159                tree.insert(
160                    component_info.clone(),
161                    FunctionDictionary {
162                        name_and_types: name_and_types.into_iter().cloned().collect(),
163                    },
164                );
165            }
166        }
167
168        if tree.is_empty() {
169            return Err(format!("interface `{interface_name}` not found"));
170        }
171
172        Ok(ComponentDependencies { dependencies: tree })
173    }
174
175    pub fn filter_by_package_name(
176        &self,
177        package_name: &PackageName,
178    ) -> Result<crate::ComponentDependencies, String> {
179        // If the package name corresponds to the root package name we pick that up
180        let mut tree = BTreeMap::new();
181
182        for (component_info, function_dict) in self.dependencies.iter() {
183            if let Some(root_package_name) = &component_info.root_package_name {
184                if root_package_name == &package_name.to_string() {
185                    tree.insert(component_info.clone(), function_dict.clone());
186                }
187            } else {
188                // If this package doesn't correspond to a root, but happens to be part of the component then
189
190                let name_and_types = function_dict
191                    .name_and_types
192                    .iter()
193                    .filter(|(f, _)| f.package_name() == Some(package_name.clone()))
194                    .collect::<Vec<_>>();
195
196                if !name_and_types.is_empty() {
197                    tree.insert(
198                        component_info.clone(),
199                        FunctionDictionary {
200                            name_and_types: name_and_types.into_iter().cloned().collect(),
201                        },
202                    );
203                }
204            }
205        }
206
207        if tree.is_empty() {
208            return Err(format!("package `{package_name}` not found"));
209        }
210
211        Ok(crate::ComponentDependencies { dependencies: tree })
212    }
213
214    pub fn filter_by_fully_qualified_interface(
215        &self,
216        fqi: &FullyQualifiedInterfaceName,
217    ) -> Result<Self, String> {
218        let mut tree = BTreeMap::new();
219
220        for (component_info, function_dict) in self.dependencies.iter() {
221            if let Some(root_package_name) = &component_info.root_package_name {
222                if root_package_name == &fqi.package_name.to_string() {
223                    tree.insert(component_info.clone(), function_dict.clone());
224                }
225            } else {
226                // If this package doesn't correspond to a root, but happens to be part of the component then
227
228                let name_and_types = function_dict
229                    .name_and_types
230                    .iter()
231                    .filter(|(f, _)| {
232                        f.package_name() == Some(fqi.package_name.clone())
233                            && f.interface_name() == Some(fqi.interface_name.clone())
234                    })
235                    .collect::<Vec<_>>();
236
237                if !name_and_types.is_empty() {
238                    tree.insert(
239                        component_info.clone(),
240                        FunctionDictionary {
241                            name_and_types: name_and_types.into_iter().cloned().collect(),
242                        },
243                    );
244                }
245            }
246        }
247
248        if tree.is_empty() {
249            return Err(format!("`{fqi}` not found"));
250        }
251
252        Ok(ComponentDependencies { dependencies: tree })
253    }
254
255    // type-parameter can be None.
256    // If present, it may represent the root package name of the component
257    // or it could represent the package or interface within a component
258    pub fn get_worker_instance_type(
259        &self,
260        type_parameter: Option<TypeParameter>,
261        worker_name: Option<Expr>,
262    ) -> Result<InstanceCreationType, String> {
263        match type_parameter {
264            None => Ok(InstanceCreationType::WitWorker {
265                component_info: None,
266                worker_name: worker_name.map(Box::new),
267            }),
268
269            Some(TypeParameter::PackageName(package_name)) => {
270                // If the user has specified the root package name, annotate the InstanceCreationType with the component already
271                let result = self
272                    .dependencies
273                    .iter()
274                    .find(|(x, _)| match &x.root_package_name {
275                        Some(name) => {
276                            let pkg = match &x.root_package_version {
277                                None => name.to_string(),
278                                Some(version) => format!("{name}@{version}"),
279                            };
280
281                            pkg == package_name.to_string()
282                        }
283
284                        None => false,
285                    });
286
287                if let Some(result) = result {
288                    Ok(InstanceCreationType::WitWorker {
289                        component_info: Some(result.0.clone()),
290                        worker_name: worker_name.map(Box::new),
291                    })
292                } else {
293                    Ok(InstanceCreationType::WitWorker {
294                        component_info: None,
295                        worker_name: worker_name.map(Box::new),
296                    })
297                }
298            }
299
300            _ => Ok(InstanceCreationType::WitWorker {
301                component_info: None,
302                worker_name: worker_name.map(Box::new),
303            }),
304        }
305    }
306
307    pub fn from_raw(
308        component_and_exports: Vec<(ComponentDependencyKey, &Vec<AnalysedExport>)>,
309    ) -> Result<Self, String> {
310        let mut dependencies = BTreeMap::new();
311
312        for (component_info, exports) in component_and_exports {
313            let function_type_registry = FunctionTypeRegistry::from_export_metadata(exports);
314            let function_dictionary =
315                FunctionDictionary::from_function_type_registry(&function_type_registry)?;
316            dependencies.insert(component_info, function_dictionary);
317        }
318
319        Ok(ComponentDependencies { dependencies })
320    }
321}