rib/
type_registry.rs

1// Copyright 2024-2025 Golem Cloud
2//
3// Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
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::call_type::CallType;
16use crate::DynamicParsedFunctionName;
17use golem_wasm_ast::analysis::AnalysedType;
18use golem_wasm_ast::analysis::{AnalysedExport, TypeVariant};
19use std::collections::{HashMap, HashSet};
20use std::fmt::{Display, Formatter};
21
22// A type-registry is a mapping from a function/variant/enum to the `arguments` and `return types` of that function/variant/enum.
23// The structure is raw and closer to the original component metadata.
24// FunctionTypeRegistry act as a set of all dependencies in Rib.
25// Currently, it talks about only 1 component.
26#[derive(Debug, PartialEq, Eq, Clone)]
27pub struct FunctionTypeRegistry {
28    pub types: HashMap<RegistryKey, RegistryValue>,
29}
30
31impl FunctionTypeRegistry {
32    pub fn get_from_keys(&self, keys: HashSet<RegistryKey>) -> FunctionTypeRegistry {
33        let mut types = HashMap::new();
34        for key in keys {
35            let registry_value = self.lookup(&key);
36            if let Some(registry_value) = registry_value {
37                types.insert(key, registry_value);
38            }
39        }
40
41        FunctionTypeRegistry { types }
42    }
43
44    pub fn get_variants(&self) -> Vec<TypeVariant> {
45        let mut variants = vec![];
46
47        for registry_value in self.types.values() {
48            if let RegistryValue::Variant { variant_type, .. } = registry_value {
49                variants.push(variant_type.clone())
50            }
51        }
52
53        variants
54    }
55
56    pub fn get(&self, key: &CallType) -> Option<&RegistryValue> {
57        match key {
58            CallType::Function { function_name, .. } => self
59                .types
60                .get(&RegistryKey::fqn_registry_key(function_name)),
61            CallType::VariantConstructor(variant_name) => self
62                .types
63                .get(&RegistryKey::FunctionName(variant_name.clone())),
64            CallType::EnumConstructor(enum_name) => self
65                .types
66                .get(&RegistryKey::FunctionName(enum_name.clone())),
67            CallType::InstanceCreation(_) => None,
68        }
69    }
70
71    pub fn empty() -> Self {
72        Self {
73            types: HashMap::new(),
74        }
75    }
76
77    pub fn from_export_metadata(exports: &Vec<AnalysedExport>) -> Self {
78        let mut map = HashMap::new();
79
80        let mut types = HashSet::new();
81
82        for export in exports {
83            match export {
84                AnalysedExport::Instance(ty) => {
85                    let interface_name = &ty.name;
86                    for fun in ty.functions.clone() {
87                        let function_name = fun.name;
88                        let parameter_types = fun
89                            .parameters
90                            .into_iter()
91                            .map(|parameter| {
92                                let analysed_type = parameter.typ;
93                                types.insert(analysed_type.clone());
94                                analysed_type
95                            })
96                            .collect::<Vec<_>>();
97
98                        let return_types = fun
99                            .results
100                            .into_iter()
101                            .map(|result| {
102                                let analysed_type = result.typ;
103                                types.insert(analysed_type.clone());
104                                analysed_type
105                            })
106                            .collect::<Vec<_>>();
107
108                        let registry_key = RegistryKey::FunctionNameWithInterface {
109                            interface_name: interface_name.clone(),
110                            function_name: function_name.clone(),
111                        };
112
113                        let registry_value = RegistryValue::Function {
114                            parameter_types,
115                            return_types,
116                        };
117
118                        map.insert(registry_key, registry_value);
119                    }
120                }
121                AnalysedExport::Function(fun0) => {
122                    let fun = fun0.clone();
123                    let function_name = fun.name;
124                    let parameter_types = fun
125                        .parameters
126                        .into_iter()
127                        .map(|parameter| {
128                            let analysed_type = parameter.typ;
129                            types.insert(analysed_type.clone());
130                            analysed_type
131                        })
132                        .collect::<Vec<_>>();
133
134                    let return_types = fun
135                        .results
136                        .into_iter()
137                        .map(|result| {
138                            let analysed_type = result.typ;
139                            types.insert(analysed_type.clone());
140                            analysed_type
141                        })
142                        .collect::<Vec<_>>();
143
144                    let registry_value = RegistryValue::Function {
145                        parameter_types,
146                        return_types,
147                    };
148
149                    let registry_key = RegistryKey::FunctionName(function_name.clone());
150
151                    map.insert(registry_key, registry_value);
152                }
153            }
154        }
155
156        for ty in types {
157            internal::update_registry(&ty, &mut map);
158        }
159
160        Self { types: map }
161    }
162
163    pub fn lookup(&self, registry_key: &RegistryKey) -> Option<RegistryValue> {
164        self.types.get(registry_key).cloned()
165    }
166}
167
168// A registry key in Rib can in include real functions including the variant constructors.
169#[derive(Hash, Eq, PartialEq, PartialOrd, Ord, Clone, Debug)]
170pub enum RegistryKey {
171    FunctionName(String),
172    FunctionNameWithInterface {
173        interface_name: String,
174        function_name: String,
175    },
176}
177
178impl Display for RegistryKey {
179    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180        match self {
181            RegistryKey::FunctionName(name) => write!(f, "Function Name: {}", name),
182            RegistryKey::FunctionNameWithInterface {
183                interface_name,
184                function_name,
185            } => write!(
186                f,
187                "Interface: {}, Function: {}",
188                interface_name, function_name
189            ),
190        }
191    }
192}
193
194impl RegistryKey {
195    // Get the function name without the interface
196    // Note that this function name can be the name of the resource constructor,
197    // or resource method, or simple function name, that correspond to the real
198    // component metadata. Examples:
199    // [constructor]shopping-cart,
200    // [method]add-to-cart,
201    // checkout
202    pub fn get_function_name(&self) -> String {
203        match self {
204            Self::FunctionName(str) => str.clone(),
205            Self::FunctionNameWithInterface { function_name, .. } => function_name.clone(),
206        }
207    }
208
209    pub fn get_interface_name(&self) -> Option<String> {
210        match self {
211            Self::FunctionName(_) => None,
212            Self::FunctionNameWithInterface { interface_name, .. } => Some(interface_name.clone()),
213        }
214    }
215
216    // A parsed function name (the one that gets invoked with a worker) can correspond
217    // to multiple registry keys. For example: this is mainly because a function can have a constructor component
218    // along with the method name (2 registry keys correspond to this 1 function).
219    // Otherwise it's only 1 key that correspond to the Fqn always.
220    pub fn registry_keys_of_function(
221        function_name: &DynamicParsedFunctionName,
222    ) -> Vec<RegistryKey> {
223        let resource_constructor_key = Self::resource_constructor_registry_key(function_name);
224        let function_name_registry_key = Self::fqn_registry_key(function_name);
225        if let Some(resource_constructor_key) = resource_constructor_key {
226            vec![resource_constructor_key, function_name_registry_key]
227        } else {
228            vec![function_name_registry_key]
229        }
230    }
231
232    // To obtain the registry key that correspond to the FQN of the function
233    // Note that, it will not provide the registry key corresponding to the constructor of a resource
234    // if the function was part of a resource
235    pub fn fqn_registry_key(function: &DynamicParsedFunctionName) -> RegistryKey {
236        let resource_method_name_in_metadata = function.function_name_with_prefix_identifiers();
237
238        match function.site.interface_name() {
239            None => RegistryKey::FunctionName(resource_method_name_in_metadata),
240            Some(interface) => RegistryKey::FunctionNameWithInterface {
241                interface_name: interface.to_string(),
242                function_name: resource_method_name_in_metadata,
243            },
244        }
245    }
246
247    // Obtain the registry-key corresponding to the resource constructor in a dynamic parsed function name
248    pub fn resource_constructor_registry_key(
249        function: &DynamicParsedFunctionName,
250    ) -> Option<RegistryKey> {
251        let resource_name_without_prefixes = function.resource_name_simplified();
252
253        resource_name_without_prefixes.map(|resource_name_without_prefix| {
254            let resource_constructor_with_prefix =
255                format!["[constructor]{}", resource_name_without_prefix];
256
257            match function.site.interface_name() {
258                None => RegistryKey::FunctionName(resource_constructor_with_prefix),
259                Some(interface) => RegistryKey::FunctionNameWithInterface {
260                    interface_name: interface.to_string(),
261                    function_name: resource_constructor_with_prefix,
262                },
263            }
264        })
265    }
266
267    pub fn from_call_type(call_type: &CallType) -> Option<RegistryKey> {
268        match call_type {
269            CallType::VariantConstructor(variant_name) => {
270                Some(RegistryKey::FunctionName(variant_name.clone()))
271            }
272            CallType::EnumConstructor(enum_name) => {
273                Some(RegistryKey::FunctionName(enum_name.clone()))
274            }
275            CallType::Function { function_name, .. } => match function_name.site.interface_name() {
276                None => Some(RegistryKey::FunctionName(
277                    function_name.function_name_with_prefix_identifiers(),
278                )),
279                Some(interface_name) => Some(RegistryKey::FunctionNameWithInterface {
280                    interface_name: interface_name.to_string(),
281                    function_name: function_name.function_name_with_prefix_identifiers(),
282                }),
283            },
284            CallType::InstanceCreation(_) => None,
285        }
286    }
287}
288
289#[derive(PartialEq, Eq, Clone, Debug)]
290pub enum RegistryValue {
291    Value(AnalysedType),
292    Variant {
293        parameter_types: Vec<AnalysedType>,
294        variant_type: TypeVariant,
295    },
296    Function {
297        parameter_types: Vec<AnalysedType>,
298        return_types: Vec<AnalysedType>,
299    },
300}
301
302impl RegistryValue {
303    pub fn argument_types(&self) -> Vec<AnalysedType> {
304        match self {
305            RegistryValue::Function {
306                parameter_types,
307                return_types: _,
308            } => parameter_types.clone(),
309            RegistryValue::Variant {
310                parameter_types,
311                variant_type: _,
312            } => parameter_types.clone(),
313            RegistryValue::Value(_) => vec![],
314        }
315    }
316}
317
318mod internal {
319    use crate::{RegistryKey, RegistryValue};
320    use golem_wasm_ast::analysis::{AnalysedType, TypeResult};
321    use std::collections::HashMap;
322
323    pub(crate) fn update_registry(
324        ty: &AnalysedType,
325        registry: &mut HashMap<RegistryKey, RegistryValue>,
326    ) {
327        match ty.clone() {
328            AnalysedType::Variant(variant) => {
329                let type_variant = variant.clone();
330                for name_type_pair in &type_variant.cases {
331                    registry.insert(RegistryKey::FunctionName(name_type_pair.name.clone()), {
332                        name_type_pair.typ.clone().map_or(
333                            RegistryValue::Value(ty.clone()),
334                            |variant_parameter_typ| RegistryValue::Variant {
335                                parameter_types: vec![variant_parameter_typ],
336                                variant_type: type_variant.clone(),
337                            },
338                        )
339                    });
340                }
341            }
342
343            AnalysedType::Enum(type_enum) => {
344                for name_type_pair in type_enum.cases {
345                    registry.insert(
346                        RegistryKey::FunctionName(name_type_pair.clone()),
347                        RegistryValue::Value(ty.clone()),
348                    );
349                }
350            }
351
352            AnalysedType::Tuple(tuple) => {
353                for element in tuple.items {
354                    update_registry(&element, registry);
355                }
356            }
357
358            AnalysedType::List(list) => {
359                update_registry(list.inner.as_ref(), registry);
360            }
361
362            AnalysedType::Record(record) => {
363                for name_type in record.fields.iter() {
364                    update_registry(&name_type.typ, registry);
365                }
366            }
367
368            AnalysedType::Result(TypeResult {
369                ok: Some(ok_type),
370                err: Some(err_type),
371            }) => {
372                update_registry(ok_type.as_ref(), registry);
373                update_registry(err_type.as_ref(), registry);
374            }
375            AnalysedType::Result(TypeResult {
376                ok: None,
377                err: Some(err_type),
378            }) => {
379                update_registry(err_type.as_ref(), registry);
380            }
381            AnalysedType::Result(TypeResult {
382                ok: Some(ok_type),
383                err: None,
384            }) => {
385                update_registry(ok_type.as_ref(), registry);
386            }
387            AnalysedType::Option(type_option) => {
388                update_registry(type_option.inner.as_ref(), registry);
389            }
390            AnalysedType::Result(TypeResult {
391                ok: None,
392                err: None,
393            }) => {}
394            AnalysedType::Flags(_) => {}
395            AnalysedType::Str(_) => {}
396            AnalysedType::Chr(_) => {}
397            AnalysedType::F64(_) => {}
398            AnalysedType::F32(_) => {}
399            AnalysedType::U64(_) => {}
400            AnalysedType::S64(_) => {}
401            AnalysedType::U32(_) => {}
402            AnalysedType::S32(_) => {}
403            AnalysedType::U16(_) => {}
404            AnalysedType::S16(_) => {}
405            AnalysedType::U8(_) => {}
406            AnalysedType::S8(_) => {}
407            AnalysedType::Bool(_) => {}
408            AnalysedType::Handle(_) => {}
409        }
410    }
411}
412
413#[cfg(feature = "protobuf")]
414mod protobuf {
415
416    use crate::RegistryKey;
417    use golem_api_grpc::proto::golem::rib::registry_key::KeyType;
418
419    impl TryFrom<golem_api_grpc::proto::golem::rib::RegistryKey> for RegistryKey {
420        type Error = String;
421
422        fn try_from(
423            value: golem_api_grpc::proto::golem::rib::RegistryKey,
424        ) -> Result<Self, Self::Error> {
425            let key_type = value.key_type.ok_or("key type missing")?;
426
427            let registry_key = match key_type {
428                KeyType::FunctionName(string) => RegistryKey::FunctionName(string.name),
429                KeyType::FunctionNameWithInterface(function_with_interface) => {
430                    let interface_name = function_with_interface.interface_name.clone();
431                    let function_name = function_with_interface.function_name;
432
433                    RegistryKey::FunctionNameWithInterface {
434                        interface_name,
435                        function_name,
436                    }
437                }
438            };
439
440            Ok(registry_key)
441        }
442    }
443
444    impl From<&RegistryKey> for golem_api_grpc::proto::golem::rib::RegistryKey {
445        fn from(value: &RegistryKey) -> Self {
446            match value {
447                RegistryKey::FunctionName(name) => golem_api_grpc::proto::golem::rib::RegistryKey {
448                    key_type: Some(KeyType::FunctionName(
449                        golem_api_grpc::proto::golem::rib::FunctionName {
450                            name: name.to_string(),
451                        },
452                    )),
453                },
454                RegistryKey::FunctionNameWithInterface {
455                    function_name,
456                    interface_name,
457                } => golem_api_grpc::proto::golem::rib::RegistryKey {
458                    key_type: Some(KeyType::FunctionNameWithInterface(
459                        golem_api_grpc::proto::golem::rib::FunctionNameWithInterface {
460                            interface_name: interface_name.clone(),
461                            function_name: function_name.clone(),
462                        },
463                    )),
464                },
465            }
466        }
467    }
468}