Skip to main content

rib/registry/
function_dictionary.rs

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