rib/
function_name.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;
16use bincode::{BorrowDecode, Decode, Encode};
17use combine::stream::position::Stream;
18use combine::{eof, EasyParser, Parser};
19use golem_wasm_rpc::{parse_value_and_type, ValueAndType};
20use semver::{BuildMetadata, Prerelease};
21use serde::{Deserialize, Serialize};
22use std::borrow::Cow;
23use std::fmt::Display;
24
25#[derive(PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
26pub struct SemVer(pub semver::Version);
27
28impl std::fmt::Debug for SemVer {
29    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30        write!(f, "{}", self.0)
31    }
32}
33
34impl Encode for SemVer {
35    fn encode<E: bincode::enc::Encoder>(
36        &self,
37        encoder: &mut E,
38    ) -> Result<(), bincode::error::EncodeError> {
39        self.0.major.encode(encoder)?;
40        self.0.minor.encode(encoder)?;
41        self.0.patch.encode(encoder)?;
42        self.0.pre.as_str().encode(encoder)?;
43        self.0.build.as_str().encode(encoder)?;
44        Ok(())
45    }
46}
47
48impl<Context> Decode<Context> for SemVer {
49    fn decode<D: bincode::de::Decoder<Context = Context>>(
50        decoder: &mut D,
51    ) -> Result<Self, bincode::error::DecodeError> {
52        let major = u64::decode(decoder)?;
53        let minor = u64::decode(decoder)?;
54        let patch = u64::decode(decoder)?;
55        let pre_str = String::decode(decoder)?;
56        let build_str = String::decode(decoder)?;
57        let pre = Prerelease::new(&pre_str)
58            .map_err(|_| bincode::error::DecodeError::OtherString("Invalid prerelease".into()))?;
59        let build = BuildMetadata::new(&build_str).map_err(|_| {
60            bincode::error::DecodeError::OtherString("Invalid build metadata".into())
61        })?;
62
63        Ok(SemVer(semver::Version {
64            major,
65            minor,
66            patch,
67            pre,
68            build,
69        }))
70    }
71}
72
73impl<'de, Context> BorrowDecode<'de, Context> for SemVer {
74    fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
75        decoder: &mut D,
76    ) -> Result<Self, bincode::error::DecodeError> {
77        let major = u64::borrow_decode(decoder)?;
78        let minor = u64::borrow_decode(decoder)?;
79        let patch = u64::borrow_decode(decoder)?;
80        let pre_str = <Cow<'de, str> as BorrowDecode<Context>>::borrow_decode(decoder)?;
81        let build_str = <Cow<'de, str> as BorrowDecode<Context>>::borrow_decode(decoder)?;
82        let pre = Prerelease::new(&pre_str)
83            .map_err(|_| bincode::error::DecodeError::OtherString("Invalid prerelease".into()))?;
84        let build = BuildMetadata::new(&build_str).map_err(|_| {
85            bincode::error::DecodeError::OtherString("Invalid build metadata".into())
86        })?;
87        Ok(SemVer(semver::Version {
88            major,
89            minor,
90            patch,
91            pre,
92            build,
93        }))
94    }
95}
96
97#[derive(Debug, Hash, PartialEq, Eq, Clone, Encode, Decode, Ord, PartialOrd)]
98pub enum ParsedFunctionSite {
99    Global,
100    Interface {
101        name: String,
102    },
103    PackagedInterface {
104        namespace: String,
105        package: String,
106        interface: String,
107        version: Option<SemVer>,
108    },
109}
110
111impl ParsedFunctionSite {
112    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
113        ParsedFunctionName::parse(format!("{}.{{x}}", name.as_ref()))
114            .map(|ParsedFunctionName { site, .. }| site)
115    }
116
117    pub fn interface_name(&self) -> Option<String> {
118        match self {
119            Self::Global => None,
120            Self::Interface { name } => Some(name.clone()),
121            Self::PackagedInterface {
122                namespace,
123                package,
124                interface,
125                version: None,
126            } => Some(format!("{namespace}:{package}/{interface}")),
127            Self::PackagedInterface {
128                namespace,
129                package,
130                interface,
131                version: Some(version),
132            } => Some(format!("{namespace}:{package}/{interface}@{}", version.0)),
133        }
134    }
135}
136
137#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
138pub enum DynamicParsedFunctionReference {
139    Function {
140        function: String,
141    },
142    RawResourceConstructor {
143        resource: String,
144    },
145    RawResourceDrop {
146        resource: String,
147    },
148    RawResourceMethod {
149        resource: String,
150        method: String,
151    },
152    RawResourceStaticMethod {
153        resource: String,
154        method: String,
155    },
156    IndexedResourceConstructor {
157        resource: String,
158        resource_params: Vec<Expr>,
159    },
160    IndexedResourceMethod {
161        resource: String,
162        resource_params: Vec<Expr>,
163        method: String,
164    },
165    IndexedResourceStaticMethod {
166        resource: String,
167        resource_params: Vec<Expr>,
168        method: String,
169    },
170    IndexedResourceDrop {
171        resource: String,
172        resource_params: Vec<Expr>,
173    },
174}
175
176impl DynamicParsedFunctionReference {
177    pub fn name_pretty(&self) -> String {
178        match self {
179            DynamicParsedFunctionReference::Function { function, .. } => function.clone(),
180            DynamicParsedFunctionReference::RawResourceConstructor { resource, .. } => {
181                resource.to_string()
182            }
183            DynamicParsedFunctionReference::RawResourceDrop { .. } => "drop".to_string(),
184            DynamicParsedFunctionReference::RawResourceMethod { method, .. } => method.to_string(),
185            DynamicParsedFunctionReference::RawResourceStaticMethod { method, .. } => {
186                method.to_string()
187            }
188            DynamicParsedFunctionReference::IndexedResourceConstructor {
189                resource,
190                resource_params,
191            } => format!(
192                "{}({})",
193                resource,
194                resource_params
195                    .iter()
196                    .map(|x| x.to_string())
197                    .collect::<Vec<_>>()
198                    .join(", ")
199            ),
200            DynamicParsedFunctionReference::IndexedResourceMethod { method, .. } => {
201                method.to_string()
202            }
203            DynamicParsedFunctionReference::IndexedResourceStaticMethod { method, .. } => {
204                method.to_string()
205            }
206            DynamicParsedFunctionReference::IndexedResourceDrop { .. } => "drop".to_string(),
207        }
208    }
209
210    fn to_static(&self) -> ParsedFunctionReference {
211        match self {
212            Self::Function { function } => ParsedFunctionReference::Function {
213                function: function.clone(),
214            },
215            Self::RawResourceConstructor { resource } => {
216                ParsedFunctionReference::RawResourceConstructor {
217                    resource: resource.clone(),
218                }
219            }
220            Self::RawResourceDrop { resource } => ParsedFunctionReference::RawResourceDrop {
221                resource: resource.clone(),
222            },
223            Self::RawResourceMethod { resource, method } => {
224                ParsedFunctionReference::RawResourceMethod {
225                    resource: resource.clone(),
226                    method: method.clone(),
227                }
228            }
229            Self::RawResourceStaticMethod { resource, method } => {
230                ParsedFunctionReference::RawResourceStaticMethod {
231                    resource: resource.clone(),
232                    method: method.clone(),
233                }
234            }
235            Self::IndexedResourceConstructor {
236                resource,
237                resource_params,
238            } => ParsedFunctionReference::IndexedResourceConstructor {
239                resource: resource.clone(),
240                resource_params: resource_params
241                    .iter()
242                    .map(|expr| expr.to_string())
243                    .collect(),
244            },
245            Self::IndexedResourceMethod {
246                resource,
247                resource_params,
248                method,
249            } => ParsedFunctionReference::IndexedResourceMethod {
250                resource: resource.clone(),
251                resource_params: resource_params
252                    .iter()
253                    .map(|expr| expr.to_string())
254                    .collect(),
255                method: method.clone(),
256            },
257            Self::IndexedResourceStaticMethod {
258                resource,
259                resource_params,
260                method,
261            } => ParsedFunctionReference::IndexedResourceStaticMethod {
262                resource: resource.clone(),
263                resource_params: resource_params
264                    .iter()
265                    .map(|expr| expr.to_string())
266                    .collect(),
267                method: method.clone(),
268            },
269            Self::IndexedResourceDrop {
270                resource,
271                resource_params,
272            } => ParsedFunctionReference::IndexedResourceDrop {
273                resource: resource.clone(),
274                resource_params: resource_params
275                    .iter()
276                    .map(|expr| expr.to_string())
277                    .collect(),
278            },
279        }
280    }
281
282    pub fn raw_resource_params_mut(&mut self) -> Option<&mut [Expr]> {
283        match self {
284            Self::IndexedResourceConstructor {
285                resource_params, ..
286            }
287            | Self::IndexedResourceMethod {
288                resource_params, ..
289            }
290            | Self::IndexedResourceStaticMethod {
291                resource_params, ..
292            }
293            | Self::IndexedResourceDrop {
294                resource_params, ..
295            } => Some(resource_params.as_mut_slice()),
296            _ => None,
297        }
298    }
299
300    pub fn raw_resource_params(&self) -> Option<&Vec<Expr>> {
301        match self {
302            Self::IndexedResourceConstructor {
303                resource_params, ..
304            }
305            | Self::IndexedResourceMethod {
306                resource_params, ..
307            }
308            | Self::IndexedResourceStaticMethod {
309                resource_params, ..
310            }
311            | Self::IndexedResourceDrop {
312                resource_params, ..
313            } => Some(resource_params),
314            _ => None,
315        }
316    }
317}
318
319#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
320pub enum ParsedFunctionReference {
321    Function {
322        function: String,
323    },
324    RawResourceConstructor {
325        resource: String,
326    },
327    RawResourceDrop {
328        resource: String,
329    },
330    RawResourceMethod {
331        resource: String,
332        method: String,
333    },
334    RawResourceStaticMethod {
335        resource: String,
336        method: String,
337    },
338    IndexedResourceConstructor {
339        resource: String,
340        resource_params: Vec<String>,
341    },
342    IndexedResourceMethod {
343        resource: String,
344        resource_params: Vec<String>,
345        method: String,
346    },
347    IndexedResourceStaticMethod {
348        resource: String,
349        resource_params: Vec<String>,
350        method: String,
351    },
352    IndexedResourceDrop {
353        resource: String,
354        resource_params: Vec<String>,
355    },
356}
357
358impl Display for ParsedFunctionReference {
359    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
360        let function_name = match self {
361            Self::Function { function } => function.clone(),
362            Self::RawResourceConstructor { resource } => format!("{resource}.new"),
363            Self::IndexedResourceConstructor {
364                resource,
365                resource_params,
366            } => {
367                format!("{}({}).new", resource, resource_params.join(", "))
368            }
369            Self::RawResourceMethod { resource, method } => format!("{resource}.{method}"),
370            Self::RawResourceStaticMethod { resource, method } => {
371                format!("[static]{resource}.{method}")
372            }
373            Self::RawResourceDrop { resource } => format!("{resource}.drop"),
374            Self::IndexedResourceDrop {
375                resource,
376                resource_params,
377            } => {
378                format!("{}({}).drop", resource, resource_params.join(", "))
379            }
380            Self::IndexedResourceMethod {
381                resource,
382                resource_params,
383                method,
384            } => {
385                format!("{}({}).{}", resource, resource_params.join(", "), method)
386            }
387            Self::IndexedResourceStaticMethod {
388                resource,
389                resource_params,
390                method,
391            } => {
392                format!(
393                    "[static]{}({}).{}",
394                    resource,
395                    resource_params.join(", "),
396                    method
397                )
398            }
399        };
400
401        write!(f, "{function_name}")
402    }
403}
404
405impl ParsedFunctionReference {
406    pub fn function_name(&self) -> String {
407        match self {
408            Self::Function { function, .. } => function.clone(),
409            Self::RawResourceConstructor { resource, .. } => format!("[constructor]{resource}"),
410            Self::RawResourceDrop { resource, .. } => format!("[drop]{resource}"),
411            Self::RawResourceMethod {
412                resource, method, ..
413            } => format!("[method]{resource}.{method}"),
414            Self::RawResourceStaticMethod {
415                resource, method, ..
416            } => format!("[static]{resource}.{method}"),
417            Self::IndexedResourceConstructor { resource, .. } => {
418                format!("[constructor]{resource}")
419            }
420            Self::IndexedResourceMethod {
421                resource, method, ..
422            } => {
423                format!("[method]{resource}.{method}")
424            }
425            Self::IndexedResourceStaticMethod {
426                resource, method, ..
427            } => {
428                format!("[static]{resource}.{method}")
429            }
430            Self::IndexedResourceDrop { resource, .. } => {
431                format!("[drop]{resource}")
432            }
433        }
434    }
435
436    pub fn resource_method_name(&self) -> Option<String> {
437        match self {
438            Self::IndexedResourceStaticMethod { method, .. }
439            | Self::RawResourceMethod { method, .. }
440            | Self::RawResourceStaticMethod { method, .. }
441            | Self::IndexedResourceMethod { method, .. } => Some(method.clone()),
442            _ => None,
443        }
444    }
445
446    pub fn method_as_static(&self) -> Option<ParsedFunctionReference> {
447        match self {
448            Self::RawResourceMethod { resource, method } => Some(Self::RawResourceStaticMethod {
449                resource: resource.clone(),
450                method: method.clone(),
451            }),
452            Self::IndexedResourceMethod {
453                resource,
454                resource_params,
455                method,
456            } => Some(Self::IndexedResourceStaticMethod {
457                resource: resource.clone(),
458                resource_params: resource_params.clone(),
459                method: method.clone(),
460            }),
461            _ => None,
462        }
463    }
464
465    pub fn is_indexed_resource(&self) -> bool {
466        matches!(
467            self,
468            Self::IndexedResourceConstructor { .. }
469                | Self::IndexedResourceMethod { .. }
470                | Self::IndexedResourceStaticMethod { .. }
471                | Self::IndexedResourceDrop { .. }
472        )
473    }
474
475    pub fn raw_resource_params(&self) -> Option<&Vec<String>> {
476        match self {
477            Self::IndexedResourceConstructor {
478                resource_params, ..
479            }
480            | Self::IndexedResourceMethod {
481                resource_params, ..
482            }
483            | Self::IndexedResourceStaticMethod {
484                resource_params, ..
485            }
486            | Self::IndexedResourceDrop {
487                resource_params, ..
488            } => Some(resource_params),
489            _ => None,
490        }
491    }
492
493    pub fn resource_name(&self) -> Option<&String> {
494        match self {
495            Self::RawResourceConstructor { resource }
496            | Self::RawResourceDrop { resource }
497            | Self::RawResourceMethod { resource, .. }
498            | Self::RawResourceStaticMethod { resource, .. }
499            | Self::IndexedResourceConstructor { resource, .. }
500            | Self::IndexedResourceMethod { resource, .. }
501            | Self::IndexedResourceStaticMethod { resource, .. }
502            | Self::IndexedResourceDrop { resource, .. } => Some(resource),
503            _ => None,
504        }
505    }
506
507    pub fn resource_params(
508        &self,
509        types: &[golem_wasm_ast::analysis::AnalysedType],
510    ) -> Result<Option<Vec<ValueAndType>>, String> {
511        if let Some(raw_params) = self.raw_resource_params() {
512            if raw_params.len() != types.len() {
513                Err(format!(
514                    "Resource params count mismatch: expected {}, got {}",
515                    types.len(),
516                    raw_params.len()
517                ))
518            } else {
519                let mut result = Vec::new();
520                for (raw_param, param_type) in raw_params.iter().zip(types.iter()) {
521                    let value_and_type: ValueAndType = parse_value_and_type(param_type, raw_param)?;
522                    result.push(value_and_type);
523                }
524                Ok(Some(result))
525            }
526        } else {
527            Ok(None)
528        }
529    }
530}
531
532// DynamicParsedFunctionName is different from ParsedFunctionName.
533// In `DynamicParsedFunctionName` the resource parameters are `Expr` (Rib) while they are `String`
534// in `ParsedFunctionName`.
535// `Expr` implies the real values are yet to be computed, while `String`
536// in ParsedFunctionName is a textual representation of the evaluated values.
537// `Examples`:
538// `DynamicParsedFunctionName` : ns:name/interface.{resource1(identifier1, { field-a: some(identifier2) }).new}
539// `ParsedFunctionName` : ns:name/interface.{resource1("foo", { field-a: some("bar") }).new}
540#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
541pub struct DynamicParsedFunctionName {
542    pub site: ParsedFunctionSite,
543    pub function: DynamicParsedFunctionReference,
544}
545
546impl DynamicParsedFunctionName {
547    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
548        let name = name.as_ref();
549
550        let mut parser = crate::parser::call::function_name();
551
552        let result = parser.easy_parse(Stream::new(name));
553
554        match result {
555            Ok((parsed, _)) => Ok(parsed),
556            Err(error) => {
557                let error_message = error.map_position(|p| p.to_string()).to_string();
558                Err(error_message)
559            }
560        }
561    }
562
563    pub fn function_name_with_prefix_identifiers(&self) -> String {
564        self.to_parsed_function_name().function.function_name()
565    }
566
567    // Usually resource name in the real metadata consist of prefixes such as [constructor]
568    // However, the one obtained through the dynamic-parsed-function-name is simple without these prefix
569    pub fn resource_name_simplified(&self) -> Option<String> {
570        self.to_parsed_function_name()
571            .function
572            .resource_name()
573            .cloned()
574    }
575
576    // Usually resource method in the real metadata consist of prefixes such as [method]
577    pub fn resource_method_name_simplified(&self) -> Option<String> {
578        self.to_parsed_function_name()
579            .function
580            .resource_method_name()
581    }
582
583    pub fn raw_resource_params_mut(&mut self) -> Option<&mut [Expr]> {
584        self.function.raw_resource_params_mut()
585    }
586
587    //
588    pub fn to_parsed_function_name(&self) -> ParsedFunctionName {
589        ParsedFunctionName {
590            site: self.site.clone(),
591            function: self.function.to_static(),
592        }
593    }
594}
595
596impl Display for DynamicParsedFunctionName {
597    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
598        let function_name = self.to_parsed_function_name().to_string();
599        write!(f, "{function_name}")
600    }
601}
602
603#[derive(Debug, PartialEq, Eq, Clone, Encode, Decode)]
604pub struct ParsedFunctionName {
605    pub site: ParsedFunctionSite,
606    pub function: ParsedFunctionReference,
607}
608
609impl Serialize for ParsedFunctionName {
610    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
611        let function_name = self.to_string();
612        serializer.serialize_str(&function_name)
613    }
614}
615
616impl<'de> Deserialize<'de> for ParsedFunctionName {
617    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
618    where
619        D: serde::Deserializer<'de>,
620    {
621        let function_name = String::deserialize(deserializer)?;
622        ParsedFunctionName::parse(function_name).map_err(serde::de::Error::custom)
623    }
624}
625
626impl Display for ParsedFunctionName {
627    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
628        let function_name = self
629            .site
630            .interface_name()
631            .map_or(self.function.function_name(), |interface| {
632                format!("{}.{{{}}}", interface, self.function)
633            });
634        write!(f, "{function_name}")
635    }
636}
637
638impl ParsedFunctionName {
639    pub fn new(site: ParsedFunctionSite, function: ParsedFunctionReference) -> Self {
640        Self { site, function }
641    }
642
643    pub fn global(name: String) -> Self {
644        Self {
645            site: ParsedFunctionSite::Global,
646            function: ParsedFunctionReference::Function { function: name },
647        }
648    }
649
650    pub fn on_interface(interface: String, function: String) -> Self {
651        Self {
652            site: ParsedFunctionSite::Interface { name: interface },
653            function: ParsedFunctionReference::Function { function },
654        }
655    }
656
657    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
658        let name = name.as_ref();
659
660        let mut parser = crate::parser::call::function_name().skip(eof());
661
662        let result = parser.easy_parse(Stream::new(name));
663
664        match result {
665            Ok((parsed, _)) => Ok(parsed.to_parsed_function_name()),
666            Err(error) => {
667                let error_message = error.map_position(|p| p.to_string()).to_string();
668                Err(error_message)
669            }
670        }
671    }
672
673    pub fn site(&self) -> &ParsedFunctionSite {
674        &self.site
675    }
676
677    pub fn function(&self) -> &ParsedFunctionReference {
678        &self.function
679    }
680
681    pub fn method_as_static(&self) -> Option<Self> {
682        self.function.method_as_static().map(|function| Self {
683            site: self.site.clone(),
684            function,
685        })
686    }
687
688    pub fn is_constructor(&self) -> Option<&str> {
689        match &self.function {
690            ParsedFunctionReference::RawResourceConstructor { resource, .. }
691            | ParsedFunctionReference::IndexedResourceConstructor { resource, .. } => {
692                Some(resource)
693            }
694            _ => None,
695        }
696    }
697
698    pub fn is_method(&self) -> Option<&str> {
699        match &self.function {
700            ParsedFunctionReference::RawResourceMethod { resource, .. }
701            | ParsedFunctionReference::IndexedResourceMethod { resource, .. }
702            | ParsedFunctionReference::RawResourceStaticMethod { resource, .. }
703            | ParsedFunctionReference::IndexedResourceStaticMethod { resource, .. } => {
704                Some(resource)
705            }
706            _ => None,
707        }
708    }
709
710    pub fn is_static_method(&self) -> Option<&str> {
711        match &self.function {
712            ParsedFunctionReference::RawResourceStaticMethod { resource, .. }
713            | ParsedFunctionReference::IndexedResourceStaticMethod { resource, .. } => {
714                Some(resource)
715            }
716            _ => None,
717        }
718    }
719}
720
721#[cfg(feature = "protobuf")]
722mod protobuf {
723    use crate::{
724        DynamicParsedFunctionName, DynamicParsedFunctionReference, Expr, ParsedFunctionName,
725        ParsedFunctionReference, ParsedFunctionSite, SemVer,
726    };
727    use golem_api_grpc::proto::golem::rib::dynamic_parsed_function_reference::FunctionReference as ProtoDynamicFunctionReference;
728    use semver::{BuildMetadata, Prerelease};
729
730    impl TryFrom<golem_api_grpc::proto::golem::rib::SemVersion> for SemVer {
731        type Error = String;
732
733        fn try_from(
734            value: golem_api_grpc::proto::golem::rib::SemVersion,
735        ) -> Result<Self, Self::Error> {
736            Ok(SemVer(semver::Version {
737                major: value.major,
738                minor: value.minor,
739                patch: value.patch,
740                pre: Prerelease::new(&value.pre).map_err(|_| "Invalid prerelease".to_string())?,
741                build: BuildMetadata::new(&value.build)
742                    .map_err(|_| "Invalid build metadata".to_string())?,
743            }))
744        }
745    }
746
747    impl From<SemVer> for golem_api_grpc::proto::golem::rib::SemVersion {
748        fn from(value: SemVer) -> Self {
749            golem_api_grpc::proto::golem::rib::SemVersion {
750                major: value.0.major,
751                minor: value.0.minor,
752                patch: value.0.patch,
753                pre: value.0.pre.to_string(),
754                build: value.0.build.to_string(),
755            }
756        }
757    }
758
759    impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionSite> for ParsedFunctionSite {
760        type Error = String;
761
762        fn try_from(
763            value: golem_api_grpc::proto::golem::rib::ParsedFunctionSite,
764        ) -> Result<Self, Self::Error> {
765            let site = value.site.ok_or("Missing site".to_string())?;
766            match site {
767                golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Global(_) => {
768                    Ok(Self::Global)
769                }
770                golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Interface(
771                    golem_api_grpc::proto::golem::rib::InterfaceFunctionSite { name },
772                ) => Ok(Self::Interface { name }),
773                golem_api_grpc::proto::golem::rib::parsed_function_site::Site::PackageInterface(
774                    golem_api_grpc::proto::golem::rib::PackageInterfaceFunctionSite {
775                        namespace,
776                        package,
777                        interface,
778                        version,
779                    },
780                ) => {
781                    let version = match version {
782                        Some(version) => Some(version.try_into()?),
783                        None => None,
784                    };
785
786                    Ok(Self::PackagedInterface {
787                        namespace,
788                        package,
789                        interface,
790                        version,
791                    })
792                }
793            }
794        }
795    }
796
797    impl From<ParsedFunctionSite> for golem_api_grpc::proto::golem::rib::ParsedFunctionSite {
798        fn from(value: ParsedFunctionSite) -> Self {
799            let site = match value {
800                ParsedFunctionSite::Global => {
801                    golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Global(
802                        golem_api_grpc::proto::golem::rib::GlobalFunctionSite {},
803                    )
804                }
805                ParsedFunctionSite::Interface { name } => {
806                    golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Interface(
807                        golem_api_grpc::proto::golem::rib::InterfaceFunctionSite { name },
808                    )
809                }
810                ParsedFunctionSite::PackagedInterface {
811                    namespace,
812                    package,
813                    interface,
814                    version,
815                } => {
816                    golem_api_grpc::proto::golem::rib::parsed_function_site::Site::PackageInterface(
817                        golem_api_grpc::proto::golem::rib::PackageInterfaceFunctionSite {
818                            namespace,
819                            package,
820                            interface,
821                            version: version.map(|v| v.into()),
822                        },
823                    )
824                }
825            };
826            golem_api_grpc::proto::golem::rib::ParsedFunctionSite { site: Some(site) }
827        }
828    }
829
830    impl From<DynamicParsedFunctionReference>
831        for golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference
832    {
833        fn from(value: DynamicParsedFunctionReference) -> Self {
834            let function = match value {
835                DynamicParsedFunctionReference::Function { function } => ProtoDynamicFunctionReference::Function(
836                    golem_api_grpc::proto::golem::rib::FunctionFunctionReference { function },
837                ),
838                DynamicParsedFunctionReference::RawResourceConstructor { resource } => ProtoDynamicFunctionReference::RawResourceConstructor(
839                    golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference { resource },
840                ),
841                DynamicParsedFunctionReference::RawResourceMethod { resource, method } => ProtoDynamicFunctionReference::RawResourceMethod(
842                    golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference { resource, method },
843                ),
844                DynamicParsedFunctionReference::RawResourceStaticMethod { resource, method } => ProtoDynamicFunctionReference::RawResourceStaticMethod(
845                    golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference { resource, method },
846                ),
847                DynamicParsedFunctionReference::RawResourceDrop { resource } => ProtoDynamicFunctionReference::RawResourceDrop(
848                    golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference { resource },
849                ),
850                DynamicParsedFunctionReference::IndexedResourceConstructor { resource, resource_params } => ProtoDynamicFunctionReference::IndexedResourceConstructor(
851                    golem_api_grpc::proto::golem::rib::DynamicIndexedResourceConstructorFunctionReference {
852                        resource,
853                        resource_params: resource_params.into_iter().map(|x| x.into()).collect(),
854                    },
855                ),
856                DynamicParsedFunctionReference::IndexedResourceMethod { resource, resource_params, method } => ProtoDynamicFunctionReference::IndexedResourceMethod(
857                    golem_api_grpc::proto::golem::rib::DynamicIndexedResourceMethodFunctionReference {
858                        resource,
859                        resource_params: resource_params.into_iter().map(|x| x.into()).collect(),
860                        method,
861                    },
862                ),
863                DynamicParsedFunctionReference::IndexedResourceStaticMethod { resource, resource_params, method } => ProtoDynamicFunctionReference::IndexedResourceStaticMethod(
864                    golem_api_grpc::proto::golem::rib::DynamicIndexedResourceStaticMethodFunctionReference {
865                        resource,
866                        resource_params: resource_params.into_iter().map(|x| x.into()).collect(),
867                        method,
868                    },
869                ),
870                DynamicParsedFunctionReference::IndexedResourceDrop { resource, resource_params } => ProtoDynamicFunctionReference::IndexedResourceDrop(
871                    golem_api_grpc::proto::golem::rib::DynamicIndexedResourceDropFunctionReference {
872                        resource,
873                        resource_params: resource_params.into_iter().map(|x| x.into()).collect(),
874                    },
875                ),
876            };
877
878            golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference {
879                function_reference: Some(function),
880            }
881        }
882    }
883
884    impl TryFrom<golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference>
885        for DynamicParsedFunctionReference
886    {
887        type Error = String;
888
889        fn try_from(
890            value: golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference,
891        ) -> Result<Self, Self::Error> {
892            let function = value
893                .function_reference
894                .ok_or("Missing function reference".to_string())?;
895
896            match function {
897                ProtoDynamicFunctionReference::Function(golem_api_grpc::proto::golem::rib::FunctionFunctionReference {
898                                                            function
899                                                        }) => {
900                    Ok(Self::Function { function })
901                }
902                ProtoDynamicFunctionReference::RawResourceConstructor(golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
903                                                                          resource
904                                                                      }) => {
905                    Ok(Self::RawResourceConstructor { resource })
906                }
907                ProtoDynamicFunctionReference::RawResourceMethod(golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
908                                                                     resource,
909                                                                     method
910                                                                 }) => {
911                    Ok(Self::RawResourceMethod { resource, method })
912                }
913                ProtoDynamicFunctionReference::RawResourceStaticMethod(golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
914                                                                           resource,
915                                                                           method
916                                                                       }) => {
917                    Ok(Self::RawResourceStaticMethod { resource, method })
918                }
919                ProtoDynamicFunctionReference::RawResourceDrop(golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference {
920                                                                   resource
921                                                               }) => {
922                    Ok(Self::RawResourceDrop { resource })
923                }
924                ProtoDynamicFunctionReference::IndexedResourceConstructor(golem_api_grpc::proto::golem::rib::DynamicIndexedResourceConstructorFunctionReference {
925                                                                              resource,
926                                                                              resource_params
927                                                                          }) => {
928                    let resource_params: Vec<Expr> =
929                        resource_params.into_iter().map(Expr::try_from).collect::<Result<Vec<Expr>, String>>()?;
930
931                    Ok(Self::IndexedResourceConstructor { resource, resource_params })
932                }
933                ProtoDynamicFunctionReference::IndexedResourceMethod(golem_api_grpc::proto::golem::rib::DynamicIndexedResourceMethodFunctionReference {
934                                                                         resource,
935                                                                         resource_params,
936                                                                         method
937                                                                     }) => {
938                    let resource_params: Vec<Expr> =
939                        resource_params.into_iter().map(Expr::try_from).collect::<Result<Vec<Expr>, String>>()?;
940
941                    Ok(Self::IndexedResourceMethod { resource, resource_params, method })
942                }
943                ProtoDynamicFunctionReference::IndexedResourceStaticMethod(golem_api_grpc::proto::golem::rib::DynamicIndexedResourceStaticMethodFunctionReference {
944                                                                               resource,
945                                                                               resource_params,
946                                                                               method
947                                                                           }) => {
948                    let resource_params: Vec<Expr> =
949                        resource_params.into_iter().map(Expr::try_from).collect::<Result<Vec<Expr>, String>>()?;
950
951                    Ok(Self::IndexedResourceStaticMethod { resource, resource_params, method })
952                }
953                ProtoDynamicFunctionReference::IndexedResourceDrop(golem_api_grpc::proto::golem::rib::DynamicIndexedResourceDropFunctionReference {
954                                                                       resource,
955                                                                       resource_params
956                                                                   }) => {
957                    let resource_params: Vec<Expr> =
958                        resource_params.into_iter().map(Expr::try_from).collect::<Result<Vec<Expr>, String>>()?;
959
960                    Ok(Self::IndexedResourceDrop { resource, resource_params })
961                }
962            }
963        }
964    }
965
966    impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionReference>
967        for ParsedFunctionReference
968    {
969        type Error = String;
970
971        fn try_from(
972            value: golem_api_grpc::proto::golem::rib::ParsedFunctionReference,
973        ) -> Result<Self, Self::Error> {
974            let function = value
975                .function_reference
976                .ok_or("Missing function".to_string())?;
977            match function {
978                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::Function(golem_api_grpc::proto::golem::rib::FunctionFunctionReference {
979                                                                                                              function
980                                                                                                          }) => {
981                    Ok(Self::Function { function })
982                }
983                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceConstructor(golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
984                                                                                                                            resource
985                                                                                                                        }) => {
986                    Ok(Self::RawResourceConstructor { resource })
987                }
988                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceMethod(golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
989                                                                                                                       resource,
990                                                                                                                       method
991                                                                                                                   }) => {
992                    Ok(Self::RawResourceMethod { resource, method })
993                }
994                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceStaticMethod(golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
995                                                                                                                             resource,
996                                                                                                                             method
997                                                                                                                         }) => {
998                    Ok(Self::RawResourceStaticMethod { resource, method })
999                }
1000                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceDrop(golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference {
1001                                                                                                                     resource
1002                                                                                                                 }) => {
1003                    Ok(Self::RawResourceDrop { resource })
1004                }
1005                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceConstructor(golem_api_grpc::proto::golem::rib::IndexedResourceConstructorFunctionReference {
1006                                                                                                                                resource,
1007                                                                                                                                resource_params
1008                                                                                                                            }) => {
1009                    Ok(Self::IndexedResourceConstructor {
1010                        resource,
1011                        resource_params,
1012                    })
1013                }
1014                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceMethod(golem_api_grpc::proto::golem::rib::IndexedResourceMethodFunctionReference {
1015                                                                                                                           resource,
1016                                                                                                                           resource_params,
1017                                                                                                                           method
1018                                                                                                                       }) => {
1019                    Ok(Self::IndexedResourceMethod {
1020                        resource,
1021                        resource_params,
1022                        method,
1023                    })
1024                }
1025                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceStaticMethod(golem_api_grpc::proto::golem::rib::IndexedResourceStaticMethodFunctionReference {
1026                                                                                                                                 resource,
1027                                                                                                                                 resource_params,
1028                                                                                                                                 method
1029                                                                                                                             }) => {
1030                    Ok(Self::IndexedResourceStaticMethod {
1031                        resource,
1032                        resource_params,
1033                        method,
1034                    })
1035                }
1036                golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceDrop(golem_api_grpc::proto::golem::rib::IndexedResourceDropFunctionReference {
1037                                                                                                                         resource,
1038                                                                                                                         resource_params
1039                                                                                                                     }) => {
1040                    Ok(Self::IndexedResourceDrop {
1041                        resource,
1042                        resource_params,
1043                    })
1044                }
1045            }
1046        }
1047    }
1048
1049    impl From<ParsedFunctionReference> for golem_api_grpc::proto::golem::rib::ParsedFunctionReference {
1050        fn from(value: ParsedFunctionReference) -> Self {
1051            let function = match value {
1052                ParsedFunctionReference::Function { function } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::Function(
1053                    golem_api_grpc::proto::golem::rib::FunctionFunctionReference { function },
1054                ),
1055                ParsedFunctionReference::RawResourceConstructor { resource } => {
1056                    golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceConstructor(
1057                        golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
1058                            resource,
1059                        },
1060                    )
1061                }
1062                ParsedFunctionReference::RawResourceMethod { resource, method } => {
1063                    golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceMethod(
1064                        golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
1065                            resource,
1066                            method,
1067                        },
1068                    )
1069                }
1070                ParsedFunctionReference::RawResourceStaticMethod { resource, method } => {
1071                    golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceStaticMethod(
1072                        golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
1073                            resource,
1074                            method,
1075                        },
1076                    )
1077                }
1078                ParsedFunctionReference::RawResourceDrop { resource } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceDrop(
1079                    golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference { resource },
1080                ),
1081                ParsedFunctionReference::IndexedResourceConstructor {
1082                    resource,
1083                    resource_params,
1084                } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceConstructor(
1085                    golem_api_grpc::proto::golem::rib::IndexedResourceConstructorFunctionReference {
1086                        resource,
1087                        resource_params,
1088                    },
1089                ),
1090                ParsedFunctionReference::IndexedResourceMethod {
1091                    resource,
1092                    resource_params,
1093                    method,
1094                } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceMethod(
1095                    golem_api_grpc::proto::golem::rib::IndexedResourceMethodFunctionReference {
1096                        resource,
1097                        resource_params,
1098                        method,
1099                    },
1100                ),
1101                ParsedFunctionReference::IndexedResourceStaticMethod {
1102                    resource,
1103                    resource_params,
1104                    method,
1105                } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceStaticMethod(
1106                    golem_api_grpc::proto::golem::rib::IndexedResourceStaticMethodFunctionReference {
1107                        resource,
1108                        resource_params,
1109                        method,
1110                    },
1111                ),
1112                ParsedFunctionReference::IndexedResourceDrop {
1113                    resource,
1114                    resource_params,
1115                } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::IndexedResourceDrop(
1116                    golem_api_grpc::proto::golem::rib::IndexedResourceDropFunctionReference {
1117                        resource,
1118                        resource_params,
1119                    },
1120                ),
1121            };
1122            golem_api_grpc::proto::golem::rib::ParsedFunctionReference {
1123                function_reference: Some(function),
1124            }
1125        }
1126    }
1127
1128    impl TryFrom<golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName>
1129        for DynamicParsedFunctionName
1130    {
1131        type Error = String;
1132
1133        fn try_from(
1134            value: golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName,
1135        ) -> Result<Self, Self::Error> {
1136            let site = ParsedFunctionSite::try_from(value.site.ok_or("Missing site".to_string())?)?;
1137            let function = DynamicParsedFunctionReference::try_from(
1138                value.function.ok_or("Missing function".to_string())?,
1139            )?;
1140            Ok(Self { site, function })
1141        }
1142    }
1143
1144    impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionName> for ParsedFunctionName {
1145        type Error = String;
1146
1147        fn try_from(
1148            value: golem_api_grpc::proto::golem::rib::ParsedFunctionName,
1149        ) -> Result<Self, Self::Error> {
1150            let site = ParsedFunctionSite::try_from(value.site.ok_or("Missing site".to_string())?)?;
1151            let function = ParsedFunctionReference::try_from(
1152                value.function.ok_or("Missing function".to_string())?,
1153            )?;
1154            Ok(Self { site, function })
1155        }
1156    }
1157
1158    impl From<ParsedFunctionName> for golem_api_grpc::proto::golem::rib::ParsedFunctionName {
1159        fn from(value: ParsedFunctionName) -> Self {
1160            golem_api_grpc::proto::golem::rib::ParsedFunctionName {
1161                site: Some(value.site.into()),
1162                function: Some(value.function.into()),
1163            }
1164        }
1165    }
1166
1167    impl From<DynamicParsedFunctionName>
1168        for golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName
1169    {
1170        fn from(value: DynamicParsedFunctionName) -> Self {
1171            golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName {
1172                site: Some(value.site.into()),
1173                function: Some(value.function.into()),
1174            }
1175        }
1176    }
1177}
1178
1179#[cfg(test)]
1180mod function_name_tests {
1181    use super::{ParsedFunctionName, ParsedFunctionReference, ParsedFunctionSite, SemVer};
1182    use golem_wasm_ast::analysis::analysed_type::{field, record, u64};
1183    use golem_wasm_rpc::Value;
1184    use test_r::test;
1185
1186    #[test]
1187    fn parse_function_name_does_not_accept_partial_matches() {
1188        let result = ParsedFunctionName::parse("x:y/z");
1189        assert!(result.is_err());
1190    }
1191
1192    #[test]
1193    fn parse_function_name_global() {
1194        let parsed = ParsedFunctionName::parse("run-example").expect("Parsing failed");
1195        assert_eq!(parsed.site().interface_name(), None);
1196        assert_eq!(parsed.function().function_name(), "run-example");
1197        assert_eq!(
1198            parsed,
1199            ParsedFunctionName {
1200                site: ParsedFunctionSite::Global,
1201                function: ParsedFunctionReference::Function {
1202                    function: "run-example".to_string()
1203                },
1204            }
1205        );
1206    }
1207
1208    #[test]
1209    fn parse_function_name_in_exported_interface_no_package() {
1210        let parsed = ParsedFunctionName::parse("interface.{fn1}").expect("Parsing failed");
1211        assert_eq!(
1212            parsed.site().interface_name(),
1213            Some("interface".to_string())
1214        );
1215        assert_eq!(parsed.function().function_name(), "fn1".to_string());
1216        assert_eq!(
1217            parsed,
1218            ParsedFunctionName {
1219                site: ParsedFunctionSite::Interface {
1220                    name: "interface".to_string()
1221                },
1222                function: ParsedFunctionReference::Function {
1223                    function: "fn1".to_string()
1224                },
1225            }
1226        );
1227    }
1228
1229    #[test]
1230    fn parse_function_name_in_exported_interface() {
1231        let parsed = ParsedFunctionName::parse("ns:name/interface.{fn1}").expect("Parsing failed");
1232        assert_eq!(
1233            parsed.site().interface_name(),
1234            Some("ns:name/interface".to_string())
1235        );
1236        assert_eq!(parsed.function().function_name(), "fn1".to_string());
1237        assert_eq!(
1238            parsed,
1239            ParsedFunctionName {
1240                site: ParsedFunctionSite::PackagedInterface {
1241                    namespace: "ns".to_string(),
1242                    package: "name".to_string(),
1243                    interface: "interface".to_string(),
1244                    version: None,
1245                },
1246                function: ParsedFunctionReference::Function {
1247                    function: "fn1".to_string()
1248                },
1249            }
1250        );
1251    }
1252
1253    #[test]
1254    fn parse_function_name_in_versioned_exported_interface() {
1255        let parsed = ParsedFunctionName::parse("wasi:cli/run@0.2.0.{run}").expect("Parsing failed");
1256        assert_eq!(
1257            parsed.site().interface_name(),
1258            Some("wasi:cli/run@0.2.0".to_string())
1259        );
1260        assert_eq!(parsed.function().function_name(), "run".to_string());
1261        assert_eq!(
1262            parsed,
1263            ParsedFunctionName {
1264                site: ParsedFunctionSite::PackagedInterface {
1265                    namespace: "wasi".to_string(),
1266                    package: "cli".to_string(),
1267                    interface: "run".to_string(),
1268                    version: Some(SemVer(semver::Version::new(0, 2, 0))),
1269                },
1270                function: ParsedFunctionReference::Function {
1271                    function: "run".to_string()
1272                },
1273            }
1274        );
1275    }
1276
1277    #[test]
1278    fn parse_function_name_constructor_syntax_sugar() {
1279        let parsed =
1280            ParsedFunctionName::parse("ns:name/interface.{resource1.new}").expect("Parsing failed");
1281        assert_eq!(
1282            parsed.site().interface_name(),
1283            Some("ns:name/interface".to_string())
1284        );
1285        assert_eq!(
1286            parsed.function().function_name(),
1287            "[constructor]resource1".to_string()
1288        );
1289        assert_eq!(
1290            parsed,
1291            ParsedFunctionName {
1292                site: ParsedFunctionSite::PackagedInterface {
1293                    namespace: "ns".to_string(),
1294                    package: "name".to_string(),
1295                    interface: "interface".to_string(),
1296                    version: None,
1297                },
1298                function: ParsedFunctionReference::RawResourceConstructor {
1299                    resource: "resource1".to_string()
1300                },
1301            }
1302        );
1303    }
1304
1305    #[test]
1306    fn parse_function_name_constructor() {
1307        let parsed = ParsedFunctionName::parse("ns:name/interface.{[constructor]resource1}")
1308            .expect("Parsing failed");
1309        assert_eq!(
1310            parsed.site().interface_name(),
1311            Some("ns:name/interface".to_string())
1312        );
1313        assert_eq!(
1314            parsed.function().function_name(),
1315            "[constructor]resource1".to_string()
1316        );
1317        assert_eq!(
1318            parsed,
1319            ParsedFunctionName {
1320                site: ParsedFunctionSite::PackagedInterface {
1321                    namespace: "ns".to_string(),
1322                    package: "name".to_string(),
1323                    interface: "interface".to_string(),
1324                    version: None,
1325                },
1326                function: ParsedFunctionReference::RawResourceConstructor {
1327                    resource: "resource1".to_string()
1328                },
1329            }
1330        );
1331    }
1332
1333    #[test]
1334    fn parse_function_name_indexed_constructor_1() {
1335        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1().new}")
1336            .expect("Parsing failed");
1337        assert_eq!(
1338            parsed.site().interface_name(),
1339            Some("ns:name/interface".to_string())
1340        );
1341        assert_eq!(
1342            parsed.function().function_name(),
1343            "[constructor]resource1".to_string()
1344        );
1345        assert!(parsed.function().is_indexed_resource());
1346        assert_eq!(parsed.function().raw_resource_params(), Some(&vec![]));
1347        assert_eq!(
1348            parsed,
1349            ParsedFunctionName {
1350                site: ParsedFunctionSite::PackagedInterface {
1351                    namespace: "ns".to_string(),
1352                    package: "name".to_string(),
1353                    interface: "interface".to_string(),
1354                    version: None,
1355                },
1356                function: ParsedFunctionReference::IndexedResourceConstructor {
1357                    resource: "resource1".to_string(),
1358                    resource_params: vec![],
1359                },
1360            }
1361        );
1362    }
1363
1364    #[test]
1365    fn parse_function_name_indexed_constructor_2() {
1366        let parsed =
1367            ParsedFunctionName::parse("ns:name/interface.{resource1(\"hello\", 1, true).new}")
1368                .expect("Parsing failed");
1369        assert_eq!(
1370            parsed.site().interface_name(),
1371            Some("ns:name/interface".to_string())
1372        );
1373        assert_eq!(
1374            parsed.function().function_name(),
1375            "[constructor]resource1".to_string()
1376        );
1377        assert!(parsed.function().is_indexed_resource());
1378        assert_eq!(
1379            parsed.function().raw_resource_params(),
1380            Some(&vec![
1381                "\"hello\"".to_string(),
1382                "1".to_string(),
1383                "true".to_string(),
1384            ])
1385        );
1386        assert_eq!(
1387            parsed,
1388            ParsedFunctionName {
1389                site: ParsedFunctionSite::PackagedInterface {
1390                    namespace: "ns".to_string(),
1391                    package: "name".to_string(),
1392                    interface: "interface".to_string(),
1393                    version: None,
1394                },
1395                function: ParsedFunctionReference::IndexedResourceConstructor {
1396                    resource: "resource1".to_string(),
1397                    resource_params: vec![
1398                        "\"hello\"".to_string(),
1399                        "1".to_string(),
1400                        "true".to_string(),
1401                    ],
1402                },
1403            },
1404        );
1405    }
1406
1407    #[test]
1408    fn parse_function_name_indexed_constructor_3() {
1409        let parsed = ParsedFunctionName::parse(
1410            "ns:name/interface.{resource1(\"hello\", { field-a: some(1) }).new}",
1411        )
1412        .expect("Parsing failed");
1413        assert_eq!(
1414            parsed.site().interface_name(),
1415            Some("ns:name/interface".to_string())
1416        );
1417        assert_eq!(
1418            parsed.function().function_name(),
1419            "[constructor]resource1".to_string()
1420        );
1421        assert!(parsed.function().is_indexed_resource());
1422        assert_eq!(
1423            parsed.function().raw_resource_params(),
1424            Some(&vec![
1425                "\"hello\"".to_string(),
1426                "{field-a: some(1)}".to_string(),
1427            ])
1428        );
1429        assert_eq!(
1430            parsed,
1431            ParsedFunctionName {
1432                site: ParsedFunctionSite::PackagedInterface {
1433                    namespace: "ns".to_string(),
1434                    package: "name".to_string(),
1435                    interface: "interface".to_string(),
1436                    version: None,
1437                },
1438                function: ParsedFunctionReference::IndexedResourceConstructor {
1439                    resource: "resource1".to_string(),
1440                    resource_params: vec![
1441                        "\"hello\"".to_string(),
1442                        "{field-a: some(1)}".to_string(),
1443                    ],
1444                },
1445            },
1446        );
1447    }
1448
1449    #[test]
1450    fn parse_function_name_indexed_method() {
1451        let parsed = ParsedFunctionName::parse(
1452            "ns:name/interface.{resource1(\"hello\", { field-a: some(1) }).something}",
1453        )
1454        .expect("Parsing failed");
1455        assert_eq!(
1456            parsed.site().interface_name(),
1457            Some("ns:name/interface".to_string())
1458        );
1459        assert_eq!(
1460            parsed.function().function_name(),
1461            "[method]resource1.something".to_string()
1462        );
1463        assert!(parsed.function().is_indexed_resource());
1464        assert_eq!(
1465            parsed.function().raw_resource_params(),
1466            Some(&vec![
1467                "\"hello\"".to_string(),
1468                "{field-a: some(1)}".to_string(),
1469            ])
1470        );
1471        assert_eq!(
1472            parsed.function().resource_method_name(),
1473            Some("something".to_string())
1474        );
1475        assert_eq!(
1476            parsed,
1477            ParsedFunctionName {
1478                site: ParsedFunctionSite::PackagedInterface {
1479                    namespace: "ns".to_string(),
1480                    package: "name".to_string(),
1481                    interface: "interface".to_string(),
1482                    version: None,
1483                },
1484                function: ParsedFunctionReference::IndexedResourceMethod {
1485                    resource: "resource1".to_string(),
1486                    resource_params: vec![
1487                        "\"hello\"".to_string(),
1488                        "{field-a: some(1)}".to_string(),
1489                    ],
1490                    method: "something".to_string(),
1491                },
1492            },
1493        );
1494    }
1495
1496    #[test]
1497    fn parse_function_name_indexed_static_method() {
1498        let parsed = ParsedFunctionName::parse(
1499            "ns:name/interface.{[static]resource1(\"hello\", { field-a: some(1) }).something}",
1500        )
1501        .expect("Parsing failed");
1502        assert_eq!(
1503            parsed.site().interface_name(),
1504            Some("ns:name/interface".to_string())
1505        );
1506        assert_eq!(
1507            parsed.function().function_name(),
1508            "[static]resource1.something".to_string()
1509        );
1510        assert!(parsed.function().is_indexed_resource());
1511        assert_eq!(
1512            parsed.function().raw_resource_params(),
1513            Some(&vec![
1514                "\"hello\"".to_string(),
1515                "{field-a: some(1)}".to_string(),
1516            ])
1517        );
1518        assert_eq!(
1519            parsed.function().resource_method_name(),
1520            Some("something".to_string())
1521        );
1522        assert_eq!(
1523            parsed,
1524            ParsedFunctionName {
1525                site: ParsedFunctionSite::PackagedInterface {
1526                    namespace: "ns".to_string(),
1527                    package: "name".to_string(),
1528                    interface: "interface".to_string(),
1529                    version: None,
1530                },
1531                function: ParsedFunctionReference::IndexedResourceStaticMethod {
1532                    resource: "resource1".to_string(),
1533                    resource_params: vec![
1534                        "\"hello\"".to_string(),
1535                        "{field-a: some(1)}".to_string(),
1536                    ],
1537                    method: "something".to_string(),
1538                },
1539            },
1540        );
1541    }
1542
1543    #[test]
1544    fn parse_function_name_method_syntax_sugar() {
1545        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something}")
1546            .expect("Parsing failed");
1547        assert_eq!(
1548            parsed.site().interface_name(),
1549            Some("ns:name/interface".to_string())
1550        );
1551        assert_eq!(
1552            parsed.function().function_name(),
1553            "[method]resource1.do-something".to_string()
1554        );
1555        assert_eq!(
1556            parsed,
1557            ParsedFunctionName {
1558                site: ParsedFunctionSite::PackagedInterface {
1559                    namespace: "ns".to_string(),
1560                    package: "name".to_string(),
1561                    interface: "interface".to_string(),
1562                    version: None,
1563                },
1564                function: ParsedFunctionReference::RawResourceMethod {
1565                    resource: "resource1".to_string(),
1566                    method: "do-something".to_string(),
1567                },
1568            }
1569        );
1570    }
1571
1572    #[test]
1573    fn parse_function_name_method() {
1574        let parsed =
1575            ParsedFunctionName::parse("ns:name/interface.{[method]resource1.do-something}")
1576                .expect("Parsing failed");
1577        assert_eq!(
1578            parsed.site().interface_name(),
1579            Some("ns:name/interface".to_string())
1580        );
1581        assert_eq!(
1582            parsed.function().function_name(),
1583            "[method]resource1.do-something".to_string()
1584        );
1585        assert_eq!(
1586            parsed,
1587            ParsedFunctionName {
1588                site: ParsedFunctionSite::PackagedInterface {
1589                    namespace: "ns".to_string(),
1590                    package: "name".to_string(),
1591                    interface: "interface".to_string(),
1592                    version: None,
1593                },
1594                function: ParsedFunctionReference::RawResourceMethod {
1595                    resource: "resource1".to_string(),
1596                    method: "do-something".to_string(),
1597                },
1598            }
1599        );
1600    }
1601
1602    #[test]
1603    fn parse_function_name_static_method_syntax_sugar() {
1604        // Note: the syntax sugared version cannot distinguish between method and static - so we need to check the actual existence of
1605        // the function and fallback.
1606        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something-static}")
1607            .expect("Parsing failed")
1608            .method_as_static()
1609            .unwrap();
1610        assert_eq!(
1611            parsed.site().interface_name(),
1612            Some("ns:name/interface".to_string())
1613        );
1614        assert_eq!(
1615            parsed.function().function_name(),
1616            "[static]resource1.do-something-static".to_string()
1617        );
1618        assert_eq!(
1619            parsed,
1620            ParsedFunctionName {
1621                site: ParsedFunctionSite::PackagedInterface {
1622                    namespace: "ns".to_string(),
1623                    package: "name".to_string(),
1624                    interface: "interface".to_string(),
1625                    version: None,
1626                },
1627                function: ParsedFunctionReference::RawResourceStaticMethod {
1628                    resource: "resource1".to_string(),
1629                    method: "do-something-static".to_string(),
1630                },
1631            }
1632        );
1633    }
1634
1635    #[test]
1636    fn parse_function_name_static() {
1637        let parsed =
1638            ParsedFunctionName::parse("ns:name/interface.{[static]resource1.do-something-static}")
1639                .expect("Parsing failed");
1640        assert_eq!(
1641            parsed.site().interface_name(),
1642            Some("ns:name/interface".to_string())
1643        );
1644        assert_eq!(
1645            parsed.function().function_name(),
1646            "[static]resource1.do-something-static".to_string()
1647        );
1648        assert_eq!(
1649            parsed,
1650            ParsedFunctionName {
1651                site: ParsedFunctionSite::PackagedInterface {
1652                    namespace: "ns".to_string(),
1653                    package: "name".to_string(),
1654                    interface: "interface".to_string(),
1655                    version: None,
1656                },
1657                function: ParsedFunctionReference::RawResourceStaticMethod {
1658                    resource: "resource1".to_string(),
1659                    method: "do-something-static".to_string(),
1660                },
1661            }
1662        );
1663    }
1664
1665    #[test]
1666    fn parse_function_name_drop_syntax_sugar() {
1667        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.drop}")
1668            .expect("Parsing failed");
1669        assert_eq!(
1670            parsed.site().interface_name(),
1671            Some("ns:name/interface".to_string())
1672        );
1673        assert_eq!(
1674            parsed.function().function_name(),
1675            "[drop]resource1".to_string()
1676        );
1677        assert_eq!(
1678            parsed,
1679            ParsedFunctionName {
1680                site: ParsedFunctionSite::PackagedInterface {
1681                    namespace: "ns".to_string(),
1682                    package: "name".to_string(),
1683                    interface: "interface".to_string(),
1684                    version: None,
1685                },
1686                function: ParsedFunctionReference::RawResourceDrop {
1687                    resource: "resource1".to_string()
1688                },
1689            }
1690        );
1691    }
1692
1693    #[test]
1694    fn parse_function_name_indexed_drop_1() {
1695        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1().drop}")
1696            .expect("Parsing failed");
1697        assert_eq!(
1698            parsed.site().interface_name(),
1699            Some("ns:name/interface".to_string())
1700        );
1701        assert_eq!(
1702            parsed.function().function_name(),
1703            "[drop]resource1".to_string()
1704        );
1705        assert!(parsed.function().is_indexed_resource());
1706        assert_eq!(parsed.function().raw_resource_params(), Some(&vec![]));
1707        assert_eq!(
1708            parsed,
1709            ParsedFunctionName {
1710                site: ParsedFunctionSite::PackagedInterface {
1711                    namespace: "ns".to_string(),
1712                    package: "name".to_string(),
1713                    interface: "interface".to_string(),
1714                    version: None,
1715                },
1716                function: ParsedFunctionReference::IndexedResourceDrop {
1717                    resource: "resource1".to_string(),
1718                    resource_params: vec![],
1719                },
1720            }
1721        )
1722    }
1723
1724    #[test]
1725    fn parse_function_name_indexed_drop_2() {
1726        let parsed =
1727            ParsedFunctionName::parse("ns:name/interface.{resource1(\"hello\", 1, true).drop}")
1728                .expect("Parsing failed");
1729        assert_eq!(
1730            parsed.site().interface_name(),
1731            Some("ns:name/interface".to_string())
1732        );
1733        assert_eq!(
1734            parsed.function().function_name(),
1735            "[drop]resource1".to_string()
1736        );
1737        assert!(parsed.function().is_indexed_resource());
1738        assert_eq!(
1739            parsed.function().raw_resource_params(),
1740            Some(&vec![
1741                "\"hello\"".to_string(),
1742                "1".to_string(),
1743                "true".to_string(),
1744            ])
1745        );
1746        assert_eq!(
1747            parsed,
1748            ParsedFunctionName {
1749                site: ParsedFunctionSite::PackagedInterface {
1750                    namespace: "ns".to_string(),
1751                    package: "name".to_string(),
1752                    interface: "interface".to_string(),
1753                    version: None,
1754                },
1755                function: ParsedFunctionReference::IndexedResourceDrop {
1756                    resource: "resource1".to_string(),
1757                    resource_params: vec![
1758                        "\"hello\"".to_string(),
1759                        "1".to_string(),
1760                        "true".to_string(),
1761                    ],
1762                },
1763            }
1764        );
1765    }
1766
1767    #[test]
1768    fn parse_function_name_indexed_drop_3() {
1769        let parsed = ParsedFunctionName::parse(
1770            "ns:name/interface.{resource1(\"hello\", { field-a: some(1) }).drop}",
1771        )
1772        .expect("Parsing failed");
1773        assert_eq!(
1774            parsed.site().interface_name(),
1775            Some("ns:name/interface".to_string())
1776        );
1777        assert_eq!(
1778            parsed.function().function_name(),
1779            "[drop]resource1".to_string()
1780        );
1781        assert!(parsed.function().is_indexed_resource());
1782        assert_eq!(
1783            parsed.function().raw_resource_params(),
1784            Some(&vec![
1785                "\"hello\"".to_string(),
1786                "{field-a: some(1)}".to_string(),
1787            ])
1788        );
1789        assert_eq!(
1790            parsed,
1791            ParsedFunctionName {
1792                site: ParsedFunctionSite::PackagedInterface {
1793                    namespace: "ns".to_string(),
1794                    package: "name".to_string(),
1795                    interface: "interface".to_string(),
1796                    version: None,
1797                },
1798                function: ParsedFunctionReference::IndexedResourceDrop {
1799                    resource: "resource1".to_string(),
1800                    resource_params: vec![
1801                        "\"hello\"".to_string(),
1802                        "{field-a: some(1)}".to_string(),
1803                    ],
1804                },
1805            },
1806        );
1807    }
1808
1809    #[test]
1810    fn parse_function_name_drop() {
1811        let parsed = ParsedFunctionName::parse("ns:name/interface.{[drop]resource1}")
1812            .expect("Parsing failed");
1813        assert_eq!(
1814            parsed.site().interface_name(),
1815            Some("ns:name/interface".to_string())
1816        );
1817        assert_eq!(
1818            parsed.function().function_name(),
1819            "[drop]resource1".to_string()
1820        );
1821        assert_eq!(
1822            parsed,
1823            ParsedFunctionName {
1824                site: ParsedFunctionSite::PackagedInterface {
1825                    namespace: "ns".to_string(),
1826                    package: "name".to_string(),
1827                    interface: "interface".to_string(),
1828                    version: None,
1829                },
1830                function: ParsedFunctionReference::RawResourceDrop {
1831                    resource: "resource1".to_string()
1832                },
1833            }
1834        );
1835    }
1836
1837    fn round_trip_function_name_parse(input: &str) {
1838        let parsed = ParsedFunctionName::parse(input)
1839            .unwrap_or_else(|_| panic!("Input Parsing failed for {input}"));
1840        let parsed_written =
1841            ParsedFunctionName::parse(parsed.to_string()).expect("Round-trip parsing failed");
1842        assert_eq!(parsed, parsed_written);
1843    }
1844
1845    #[test]
1846    fn test_parsed_function_name_display() {
1847        round_trip_function_name_parse("run-example");
1848        round_trip_function_name_parse("interface.{fn1}");
1849        round_trip_function_name_parse("wasi:cli/run@0.2.0.{run}");
1850        round_trip_function_name_parse("ns:name/interface.{resource1.new}");
1851        round_trip_function_name_parse("ns:name/interface.{[constructor]resource1}");
1852        round_trip_function_name_parse("ns:name/interface.{resource1().new}");
1853        round_trip_function_name_parse("ns:name/interface.{resource1(\"hello\", 1, true).new}");
1854        round_trip_function_name_parse(
1855            "ns:name/interface.{resource1(\"hello\", { field-a: some(1) }).new}",
1856        );
1857        round_trip_function_name_parse("ns:name/interface.{resource1.do-something}");
1858        round_trip_function_name_parse(
1859            "ns:name/interface.{resource1(\"hello\", 1, true).do-something}",
1860        );
1861        round_trip_function_name_parse(
1862            "ns:name/interface.{resource1(\"hello\", 1, { field-a: some(1) }).do-something}",
1863        );
1864        round_trip_function_name_parse("ns:name/interface.{[static]resource1.do-something-static}");
1865        round_trip_function_name_parse(
1866            "ns:name/interface.{[static]resource1(\"hello\", 1, true).do-something-static}",
1867        );
1868        round_trip_function_name_parse("ns:name/interface.{[static]resource1(\"hello\", 1, { field-a: some(1) }).do-something-static}");
1869        round_trip_function_name_parse("ns:name/interface.{resource1.drop}");
1870        round_trip_function_name_parse("ns:name/interface.{resource1().drop}");
1871        round_trip_function_name_parse("ns:name/interface.{resource1(\"hello\", 1, true).drop}");
1872        round_trip_function_name_parse(
1873            "ns:name/interface.{resource1(\"hello\", { field-a: some(1) }).drop}",
1874        );
1875        round_trip_function_name_parse("ns:name/interface.{[drop]resource1}");
1876    }
1877
1878    #[test]
1879    fn test_parsed_function_name_complex_resource_args() {
1880        round_trip_function_name_parse(
1881            r#"golem:api/oplog-processor@1.1.0-rc1.{processor({ account-id: { value: "-1" } }, { high-bits: 11637111831105389641, low-bits: 11277240687824975272 }, []).process}"#,
1882        )
1883    }
1884
1885    #[test]
1886    fn test_parsed_function_name_complex_resource_args_large_nums() {
1887        let parsed = ParsedFunctionName::parse(r#"golem:api/oplog-processor@1.1.0-rc1.{processor({ high-bits: 18389549593665948372, low-bits: 12287617583649128209 }).process}"#).expect("Input Parsing failed");
1888        let args = parsed
1889            .function
1890            .resource_params(&[record(vec![
1891                field("high-bits", u64()),
1892                field("low-bits", u64()),
1893            ])])
1894            .expect("Resource params parsing failed")
1895            .expect("Resource params not found");
1896        let nums = if let Value::Record(nums) = &args[0].value {
1897            nums.clone()
1898        } else {
1899            panic!("Expected record")
1900        };
1901
1902        assert_eq!(
1903            nums,
1904            vec![
1905                Value::U64(18389549593665948372u64),
1906                Value::U64(12287617583649128209u64),
1907            ]
1908        )
1909    }
1910}