rib/
function_name.rs

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