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