rib/registry/
function_dictionary.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, TypeParameter};
16use crate::type_parameter::InterfaceName;
17use crate::{
18    CallType, DynamicParsedFunctionName, DynamicParsedFunctionReference, FunctionTypeRegistry,
19    InferredType, ParsedFunctionSite, RegistryKey, RegistryValue, SemVer,
20};
21use golem_wasm_ast::analysis::{AnalysedExport, AnalysedType, TypeEnum, TypeVariant};
22use std::collections::BTreeMap;
23use std::convert::TryFrom;
24use std::fmt::{Debug, Display, Formatter};
25
26// Global Function Dictionary is a user friendly projection of FunctionTypeRegistry for functions and arguments.
27// In fact, type inference phases make use of FunctionDictionary.
28// Unlike FunctionTypeRegistry, the function names in `FunctionDictionary` is closer to Rib grammar
29// of invoking functions. Example: A RegistryKey of `[constructor]cart` in FunctionTypeRegistry becomes
30// FunctionName::ResourceConstructor(cart) in FunctionDictionary
31#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd, Default)]
32pub struct FunctionDictionary {
33    pub name_and_types: Vec<(FunctionName, FunctionType)>,
34}
35
36impl FunctionDictionary {
37    pub fn get(&self, function_name: &FunctionName) -> Option<&FunctionType> {
38        self.name_and_types.iter().find_map(|(name, ftype)| {
39            if name == function_name {
40                Some(ftype)
41            } else {
42                None
43            }
44        })
45    }
46
47    pub fn get_all_variants(&self) -> Vec<TypeVariant> {
48        self.name_and_types
49            .iter()
50            .filter_map(|(_, ftype)| ftype.as_type_variant())
51            .collect()
52    }
53
54    pub fn get_all_enums(&self) -> Vec<TypeEnum> {
55        self.name_and_types
56            .iter()
57            .filter_map(|(_, ftype)| ftype.as_type_enum())
58            .collect()
59    }
60
61    pub fn get_enum_info(&self, identifier_name: &str) -> Option<TypeEnum> {
62        self.name_and_types.iter().find_map(|(f, ftype)| match f {
63            FunctionName::Enum(name) => {
64                if name == identifier_name {
65                    ftype.as_type_enum()
66                } else {
67                    None
68                }
69            }
70            _ => None,
71        })
72    }
73    pub fn get_variant_info(&self, identifier_name: &str) -> Option<TypeVariant> {
74        self.name_and_types.iter().find_map(|(f, ftype)| match f {
75            FunctionName::Variant(name) => {
76                if name == identifier_name {
77                    ftype.as_type_variant()
78                } else {
79                    None
80                }
81            }
82            _ => None,
83        })
84    }
85
86    pub fn function_names(&self) -> Vec<String> {
87        self.name_and_types
88            .iter()
89            .map(|(f, _)| f.name())
90            .collect::<Vec<_>>()
91    }
92}
93
94// A `ResourceMethodDictionary` is a typesafe subset or projection of resource methods in
95// `FunctionDictionary`.
96// The `InstanceType` holds resource method dictionary instead of a full function method dictionary,
97// if the instance is a resource creation.
98// Given the Dictionaries do become part of InferredType (InferredType::InstanceType::Dictionaries)
99// order of component loading into the rib context shouldn't change it's type.
100#[derive(Debug, Hash, Clone, Eq, PartialEq, PartialOrd, Ord)]
101pub struct ResourceMethodDictionary {
102    pub map: BTreeMap<FullyQualifiedResourceMethod, FunctionType>,
103}
104
105impl From<&ResourceMethodDictionary> for FunctionDictionary {
106    fn from(value: &ResourceMethodDictionary) -> Self {
107        FunctionDictionary {
108            name_and_types: value
109                .map
110                .iter()
111                .map(|(key, value)| (FunctionName::ResourceMethod(key.clone()), value.clone()))
112                .collect(),
113        }
114    }
115}
116
117impl FunctionDictionary {
118    pub fn from_exports(exports: &Vec<AnalysedExport>) -> Result<FunctionDictionary, String> {
119        let registry = FunctionTypeRegistry::from_export_metadata(exports);
120        Self::from_function_type_registry(&registry)
121    }
122
123    pub fn from_function_type_registry(
124        registry: &FunctionTypeRegistry,
125    ) -> Result<FunctionDictionary, String> {
126        let mut map = vec![];
127
128        for (key, value) in registry.types.iter() {
129            match value {
130                RegistryValue::Function {
131                    parameter_types,
132                    return_type,
133                } => match key {
134                    RegistryKey::FunctionName(function_name) => {
135                        let function_name = resolve_function_name(None, None, function_name)?;
136
137                        map.push((
138                            function_name,
139                            FunctionType {
140                                parameter_types: parameter_types.iter().map(|x| x.into()).collect(),
141                                return_type: return_type.as_ref().map(|x| x.into()),
142                            },
143                        ));
144                    }
145
146                    RegistryKey::FunctionNameWithInterface {
147                        interface_name,
148                        function_name,
149                    } => {
150                        let type_parameter = TypeParameter::from_text(interface_name.as_str())?;
151
152                        let interface_name = type_parameter.get_interface_name();
153                        let package_name = type_parameter.get_package_name();
154
155                        let function_name =
156                            resolve_function_name(package_name, interface_name, function_name)?;
157
158                        map.push((
159                            function_name,
160                            FunctionType {
161                                parameter_types: parameter_types.iter().map(|x| x.into()).collect(),
162                                return_type: return_type.as_ref().map(|x| x.into()),
163                            },
164                        ));
165                    }
166                },
167
168                RegistryValue::Variant {
169                    parameter_types,
170                    variant_type,
171                } => match key {
172                    RegistryKey::FunctionName(name) => {
173                        let function_name = FunctionName::Variant(name.to_string());
174                        let cases = variant_type
175                            .cases
176                            .iter()
177                            .map(|x| (x.name.clone(), x.typ.as_ref().map(InferredType::from)))
178                            .collect::<Vec<_>>();
179
180                        map.push((
181                            function_name,
182                            FunctionType {
183                                parameter_types: parameter_types.iter().map(|x| x.into()).collect(),
184                                return_type: Some(InferredType::variant(cases)),
185                            },
186                        ));
187                    }
188                    RegistryKey::FunctionNameWithInterface { .. } => {}
189                },
190
191                RegistryValue::Value(value) => match value {
192                    AnalysedType::Enum(type_enum) => match key {
193                        RegistryKey::FunctionName(name) => {
194                            let function_name = FunctionName::Enum(name.to_string());
195
196                            map.push((
197                                function_name,
198                                FunctionType {
199                                    parameter_types: vec![],
200                                    return_type: Some(InferredType::enum_(type_enum.cases.clone())),
201                                },
202                            ));
203                        }
204                        RegistryKey::FunctionNameWithInterface { .. } => {}
205                    },
206                    AnalysedType::Variant(variant_type) => match key {
207                        RegistryKey::FunctionName(name) => {
208                            let function_name = FunctionName::Variant(name.to_string());
209
210                            let cases = variant_type
211                                .cases
212                                .iter()
213                                .map(|x| (x.name.clone(), x.typ.as_ref().map(InferredType::from)))
214                                .collect::<Vec<_>>();
215
216                            map.push((
217                                function_name,
218                                FunctionType {
219                                    parameter_types: vec![],
220                                    return_type: Some(InferredType::variant(cases)),
221                                },
222                            ));
223                        }
224                        RegistryKey::FunctionNameWithInterface { .. } => {}
225                    },
226
227                    _ => {}
228                },
229            };
230        }
231
232        Ok(FunctionDictionary {
233            name_and_types: map,
234        })
235    }
236}
237
238fn resolve_function_name(
239    package_name: Option<PackageName>,
240    interface_name: Option<InterfaceName>,
241    function_name: &str,
242) -> Result<FunctionName, String> {
243    match get_resource_name(function_name) {
244        Some(resource_name) => Ok(FunctionName::ResourceConstructor(
245            FullyQualifiedResourceConstructor {
246                package_name,
247                interface_name,
248                resource_name,
249            },
250        )),
251        None => match get_resource_method_name(function_name) {
252            Ok(Some((constructor, method))) => {
253                Ok(FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
254                    package_name,
255                    interface_name,
256                    resource_name: constructor,
257                    method_name: method,
258                }))
259            }
260            Ok(None) => Ok(FunctionName::Function(FullyQualifiedFunctionName {
261                package_name,
262                interface_name,
263                function_name: function_name.to_string(),
264            })),
265
266            Err(e) => Err(format!("invalid function call. {e}")),
267        },
268    }
269}
270
271fn get_resource_name(function_name: &str) -> Option<String> {
272    if function_name.trim().starts_with("[constructor]") {
273        Some(
274            function_name
275                .trim_start_matches("[constructor]")
276                .to_string(),
277        )
278    } else {
279        None
280    }
281}
282
283fn get_resource_method_name(function_name: &str) -> Result<Option<(String, String)>, String> {
284    if function_name.starts_with("[method]") {
285        let constructor_and_method = function_name.trim_start_matches("[method]").to_string();
286        let mut constructor_and_method = constructor_and_method.split('.');
287        let constructor = constructor_and_method.next();
288        let method = constructor_and_method.next();
289
290        match (constructor, method) {
291            (Some(constructor), Some(method)) => {
292                Ok(Some((constructor.to_string(), method.to_string())))
293            }
294            _ => Err(format!("Invalid resource method name: {function_name}")),
295        }
296    } else if function_name.starts_with("[drop]") {
297        let constructor = function_name.trim_start_matches("[drop]").to_string();
298        Ok(Some((constructor, "drop".to_string())))
299    } else {
300        Ok(None)
301    }
302}
303
304#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd)]
305pub enum FunctionName {
306    Variant(String),
307    Enum(String),
308    Function(FullyQualifiedFunctionName),
309    ResourceConstructor(FullyQualifiedResourceConstructor),
310    ResourceMethod(FullyQualifiedResourceMethod),
311}
312
313impl Display for FunctionName {
314    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
315        write!(f, "{}", self.name())
316    }
317}
318
319impl FunctionName {
320    pub fn from_dynamic_parsed_function_name(
321        function_name: &DynamicParsedFunctionName,
322    ) -> FunctionName {
323        let site = &function_name.site;
324        let (package_name, interface_name) = match site {
325            ParsedFunctionSite::Global => (None, None),
326
327            ParsedFunctionSite::Interface { name } => (
328                None,
329                Some(InterfaceName {
330                    name: name.clone(),
331                    version: None,
332                }),
333            ),
334            ParsedFunctionSite::PackagedInterface {
335                namespace,
336                package,
337                interface,
338                version,
339            } => (
340                Some(PackageName {
341                    namespace: namespace.clone(),
342                    package_name: package.clone(),
343                    version: None,
344                }),
345                Some(InterfaceName {
346                    name: interface.clone(),
347                    version: version.as_ref().map(|v| v.0.to_string()),
348                }),
349            ),
350        };
351
352        match &function_name.function {
353            DynamicParsedFunctionReference::Function { function } => {
354                FunctionName::Function(FullyQualifiedFunctionName {
355                    package_name,
356                    interface_name,
357                    function_name: function.clone(),
358                })
359            }
360            DynamicParsedFunctionReference::RawResourceConstructor { resource } => {
361                FunctionName::ResourceConstructor(FullyQualifiedResourceConstructor {
362                    package_name,
363                    interface_name,
364                    resource_name: resource.clone(),
365                })
366            }
367            DynamicParsedFunctionReference::RawResourceDrop { resource } => {
368                FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
369                    package_name,
370                    interface_name,
371                    resource_name: resource.clone(),
372                    method_name: "drop".to_string(),
373                })
374            }
375            DynamicParsedFunctionReference::RawResourceMethod { resource, method } => {
376                FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
377                    package_name,
378                    interface_name,
379                    resource_name: resource.clone(),
380                    method_name: method.clone(),
381                })
382            }
383            DynamicParsedFunctionReference::RawResourceStaticMethod { resource, method } => {
384                FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
385                    package_name,
386                    interface_name,
387                    resource_name: resource.clone(),
388                    method_name: method.clone(),
389                })
390            }
391            DynamicParsedFunctionReference::IndexedResourceConstructor { resource, .. } => {
392                FunctionName::ResourceConstructor(FullyQualifiedResourceConstructor {
393                    package_name,
394                    interface_name,
395                    resource_name: resource.clone(),
396                })
397            }
398            DynamicParsedFunctionReference::IndexedResourceMethod {
399                resource, method, ..
400            } => FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
401                package_name,
402                interface_name,
403                resource_name: resource.clone(),
404                method_name: method.clone(),
405            }),
406            DynamicParsedFunctionReference::IndexedResourceStaticMethod {
407                resource,
408                method,
409                ..
410            } => FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
411                package_name,
412                interface_name,
413                resource_name: resource.clone(),
414                method_name: method.clone(),
415            }),
416            DynamicParsedFunctionReference::IndexedResourceDrop { resource, .. } => {
417                FunctionName::ResourceMethod(FullyQualifiedResourceMethod {
418                    package_name,
419                    interface_name,
420                    resource_name: resource.clone(),
421                    method_name: "drop".to_string(),
422                })
423            }
424        }
425    }
426
427    pub fn from_call_type(call_type: &CallType) -> Option<FunctionName> {
428        match call_type {
429            CallType::VariantConstructor(variant_name) => {
430                Some(FunctionName::Variant(variant_name.clone()))
431            }
432            CallType::EnumConstructor(enum_name) => Some(FunctionName::Enum(enum_name.clone())),
433            CallType::Function { function_name, .. } => {
434                Some(Self::from_dynamic_parsed_function_name(function_name))
435            }
436            CallType::InstanceCreation(_) => None,
437        }
438    }
439
440    pub fn interface_name(&self) -> Option<InterfaceName> {
441        match self {
442            FunctionName::Function(fqfn) => fqfn.interface_name.clone(),
443            FunctionName::ResourceConstructor(fqfn) => fqfn.interface_name.clone(),
444            FunctionName::ResourceMethod(resource_method) => resource_method.interface_name.clone(),
445            FunctionName::Variant(_) => None,
446            FunctionName::Enum(_) => None,
447        }
448    }
449
450    pub fn package_name(&self) -> Option<PackageName> {
451        match self {
452            FunctionName::Function(fqfn) => fqfn.package_name.clone(),
453            FunctionName::ResourceConstructor(fqfn) => fqfn.package_name.clone(),
454            FunctionName::ResourceMethod(fqfn) => fqfn.package_name.clone(),
455            FunctionName::Variant(_) => None,
456            FunctionName::Enum(_) => None,
457        }
458    }
459
460    pub fn name(&self) -> String {
461        match self {
462            FunctionName::Function(fqfn) => fqfn.function_name.to_string(),
463            FunctionName::ResourceConstructor(fqfn) => fqfn.resource_name.to_string(),
464            FunctionName::ResourceMethod(fqfn) => fqfn.method_name.to_string(),
465            FunctionName::Variant(name) => name.clone(),
466            FunctionName::Enum(name) => name.clone(),
467        }
468    }
469}
470
471#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd)]
472pub struct FullyQualifiedResourceConstructor {
473    pub package_name: Option<PackageName>,
474    pub interface_name: Option<InterfaceName>,
475    pub resource_name: String,
476}
477
478impl FullyQualifiedResourceConstructor {
479    pub fn parsed_function_site(&self) -> ParsedFunctionSite {
480        if let Some(package_name) = &self.package_name {
481            let interface_name = self.interface_name.clone().unwrap();
482
483            ParsedFunctionSite::PackagedInterface {
484                namespace: package_name.namespace.clone(),
485                package: package_name.package_name.clone(),
486                interface: self
487                    .interface_name
488                    .as_ref()
489                    .map_or_else(|| "".to_string(), |i| i.name.clone()),
490                version: interface_name
491                    .version
492                    .map(|x| SemVer(semver::Version::parse(&x).unwrap())),
493            }
494        } else if let Some(interface_name) = &self.interface_name {
495            ParsedFunctionSite::Interface {
496                name: interface_name.name.clone(),
497            }
498        } else {
499            ParsedFunctionSite::Global
500        }
501    }
502}
503
504#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd)]
505pub struct FullyQualifiedFunctionName {
506    pub package_name: Option<PackageName>,
507    pub interface_name: Option<InterfaceName>,
508    pub function_name: String,
509}
510
511#[derive(Debug, Hash, Clone, Eq, PartialEq, Ord, PartialOrd)]
512pub struct FullyQualifiedResourceMethod {
513    pub package_name: Option<PackageName>,
514    pub interface_name: Option<InterfaceName>,
515    pub resource_name: String,
516    pub method_name: String,
517}
518
519impl FullyQualifiedResourceMethod {
520    pub fn get_constructor(&self) -> FullyQualifiedResourceConstructor {
521        FullyQualifiedResourceConstructor {
522            package_name: self.package_name.clone(),
523            interface_name: self.interface_name.clone(),
524            resource_name: self.resource_name.clone(),
525        }
526    }
527
528    // We rely on the fully parsed function name itself to retrieve the original function name
529    pub fn dynamic_parsed_function_name(&self) -> Result<DynamicParsedFunctionName, String> {
530        let mut dynamic_parsed_str = String::new();
531
532        // Construct the package/interface prefix
533        if let Some(package) = &self.package_name {
534            dynamic_parsed_str.push_str(&package.to_string());
535            dynamic_parsed_str.push('/');
536        }
537
538        if let Some(interface) = &self.interface_name {
539            dynamic_parsed_str.push_str(&interface.to_string());
540            dynamic_parsed_str.push('.');
541        }
542
543        // Start the dynamic function name with resource
544        dynamic_parsed_str.push('{');
545        dynamic_parsed_str.push_str(&self.resource_name);
546        dynamic_parsed_str.push('.');
547        dynamic_parsed_str.push_str(&self.method_name);
548        dynamic_parsed_str.push('}');
549
550        DynamicParsedFunctionName::parse(dynamic_parsed_str)
551    }
552
553    pub fn method_name(&self) -> &String {
554        &self.method_name
555    }
556}
557
558impl Display for FullyQualifiedFunctionName {
559    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
560        if let Some(package_name) = &self.package_name {
561            write!(f, "{package_name}")?
562        }
563
564        if let Some(interface_name) = &self.interface_name {
565            write!(f, "/{interface_name}.")?;
566            write!(f, "{{{}}}", self.function_name)
567        } else {
568            write!(f, "{}", self.function_name)
569        }
570    }
571}
572
573#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
574pub struct FunctionType {
575    pub parameter_types: Vec<InferredType>,
576    pub return_type: Option<InferredType>,
577}
578
579impl FunctionType {
580    pub fn as_type_variant(&self) -> Option<TypeVariant> {
581        let analysed_type = AnalysedType::try_from(&self.return_type.clone()?).ok()?;
582
583        match analysed_type {
584            AnalysedType::Variant(type_variant) => Some(type_variant),
585            _ => None,
586        }
587    }
588
589    pub fn as_type_enum(&self) -> Option<TypeEnum> {
590        let analysed_type = AnalysedType::try_from(&self.return_type.clone()?).ok()?;
591        match analysed_type {
592            AnalysedType::Enum(type_enum) => Some(type_enum),
593            _ => None,
594        }
595    }
596
597    pub fn parameter_types(&self) -> Vec<AnalysedType> {
598        self.parameter_types
599            .iter()
600            .map(|x| AnalysedType::try_from(x).unwrap())
601            .collect()
602    }
603
604    pub fn return_type(&self) -> Option<AnalysedType> {
605        self.return_type
606            .clone()
607            .map(|x| AnalysedType::try_from(&x).unwrap())
608    }
609}
610
611#[cfg(feature = "protobuf")]
612mod protobuf {
613    use crate::FunctionName;
614
615    impl TryFrom<golem_api_grpc::proto::golem::rib::function_name_type::FunctionName> for FunctionName {
616        type Error = String;
617
618        fn try_from(
619            value: golem_api_grpc::proto::golem::rib::function_name_type::FunctionName,
620        ) -> Result<Self, Self::Error> {
621            match value {
622                golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::VariantName(name) => {
623                    Ok(FunctionName::Variant(name))
624                }
625                golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::EnumName(name) => {
626                    Ok(FunctionName::Enum(name))
627                }
628                golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::Function(fqfn) => {
629                    Ok(FunctionName::Function(fqfn.try_into()?))
630                }
631                golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::ResourceConstructor(fqrc) => {
632                    Ok(FunctionName::ResourceConstructor(fqrc.try_into()?))
633                }
634                golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::ResourceMethod(fqrm) => {
635                    Ok(FunctionName::ResourceMethod(fqrm.try_into()?))
636                }
637            }
638        }
639    }
640
641    impl From<FunctionName> for golem_api_grpc::proto::golem::rib::function_name_type::FunctionName {
642        fn from(value: FunctionName) -> Self {
643            match value {
644                FunctionName::Variant(name) => {
645                    golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::VariantName(name)
646                }
647                FunctionName::Enum(name) => {
648                    golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::EnumName(name)
649                }
650                FunctionName::Function(fqfn) => {
651                    golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::Function(fqfn.into())
652                }
653                FunctionName::ResourceConstructor(fqrc) => {
654                    golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::ResourceConstructor(fqrc.into())
655                }
656                FunctionName::ResourceMethod(fqrm) => {
657                    golem_api_grpc::proto::golem::rib::function_name_type::FunctionName::ResourceMethod(fqrm.into())
658                }
659            }
660        }
661    }
662}