Skip to main content

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