Skip to main content

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 combine::stream::position::Stream;
16use combine::{eof, EasyParser, Parser};
17
18use serde::{Deserialize, Serialize};
19use std::fmt::Display;
20
21#[derive(PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
22pub struct SemVer(pub semver::Version);
23
24impl SemVer {
25    pub fn parse(version: &str) -> Result<Self, String> {
26        semver::Version::parse(version)
27            .map(SemVer)
28            .map_err(|e| format!("Invalid semver string: {e}"))
29    }
30}
31
32impl std::fmt::Debug for SemVer {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
39pub enum ParsedFunctionSite {
40    Global,
41    Interface {
42        name: String,
43    },
44    PackagedInterface {
45        namespace: String,
46        package: String,
47        interface: String,
48        version: Option<SemVer>,
49    },
50}
51
52impl ParsedFunctionSite {
53    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
54        ParsedFunctionName::parse(format!("{}.{{x}}", name.as_ref()))
55            .map(|ParsedFunctionName { site, .. }| site)
56    }
57
58    pub fn interface_name(&self) -> Option<String> {
59        match self {
60            Self::Global => None,
61            Self::Interface { name } => Some(name.clone()),
62            Self::PackagedInterface {
63                namespace,
64                package,
65                interface,
66                version: None,
67            } => Some(format!("{namespace}:{package}/{interface}")),
68            Self::PackagedInterface {
69                namespace,
70                package,
71                interface,
72                version: Some(version),
73            } => Some(format!("{namespace}:{package}/{interface}@{}", version.0)),
74        }
75    }
76
77    pub fn unversioned(&self) -> ParsedFunctionSite {
78        match self {
79            ParsedFunctionSite::Global => ParsedFunctionSite::Global,
80            ParsedFunctionSite::Interface { name } => {
81                ParsedFunctionSite::Interface { name: name.clone() }
82            }
83            ParsedFunctionSite::PackagedInterface {
84                namespace,
85                package,
86                interface,
87                version: _,
88            } => ParsedFunctionSite::PackagedInterface {
89                namespace: namespace.clone(),
90                package: package.clone(),
91                interface: interface.clone(),
92                version: None,
93            },
94        }
95    }
96}
97
98#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
99pub enum DynamicParsedFunctionReference {
100    Function { function: String },
101    RawResourceConstructor { resource: String },
102    RawResourceDrop { resource: String },
103    RawResourceMethod { resource: String, method: String },
104    RawResourceStaticMethod { resource: String, method: String },
105}
106
107impl DynamicParsedFunctionReference {
108    pub fn name_pretty(&self) -> String {
109        match self {
110            DynamicParsedFunctionReference::Function { function, .. } => function.clone(),
111            DynamicParsedFunctionReference::RawResourceConstructor { resource, .. } => {
112                resource.to_string()
113            }
114            DynamicParsedFunctionReference::RawResourceDrop { .. } => "drop".to_string(),
115            DynamicParsedFunctionReference::RawResourceMethod { method, .. } => method.to_string(),
116            DynamicParsedFunctionReference::RawResourceStaticMethod { method, .. } => {
117                method.to_string()
118            }
119        }
120    }
121
122    fn to_static(&self) -> ParsedFunctionReference {
123        match self {
124            Self::Function { function } => ParsedFunctionReference::Function {
125                function: function.clone(),
126            },
127            Self::RawResourceConstructor { resource } => {
128                ParsedFunctionReference::RawResourceConstructor {
129                    resource: resource.clone(),
130                }
131            }
132            Self::RawResourceDrop { resource } => ParsedFunctionReference::RawResourceDrop {
133                resource: resource.clone(),
134            },
135            Self::RawResourceMethod { resource, method } => {
136                ParsedFunctionReference::RawResourceMethod {
137                    resource: resource.clone(),
138                    method: method.clone(),
139                }
140            }
141            Self::RawResourceStaticMethod { resource, method } => {
142                ParsedFunctionReference::RawResourceStaticMethod {
143                    resource: resource.clone(),
144                    method: method.clone(),
145                }
146            }
147        }
148    }
149}
150
151#[derive(Debug, PartialEq, Eq, Clone, Hash)]
152pub enum ParsedFunctionReference {
153    Function { function: String },
154    RawResourceConstructor { resource: String },
155    RawResourceDrop { resource: String },
156    RawResourceMethod { resource: String, method: String },
157    RawResourceStaticMethod { resource: String, method: String },
158}
159
160impl Display for ParsedFunctionReference {
161    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162        let function_name = match self {
163            Self::Function { function } => function.clone(),
164            Self::RawResourceConstructor { resource } => format!("{resource}.new"),
165            Self::RawResourceMethod { resource, method } => format!("{resource}.{method}"),
166            Self::RawResourceStaticMethod { resource, method } => {
167                format!("[static]{resource}.{method}")
168            }
169            Self::RawResourceDrop { resource } => format!("{resource}.drop"),
170        };
171
172        write!(f, "{function_name}")
173    }
174}
175
176impl ParsedFunctionReference {
177    pub fn function_name(&self) -> String {
178        match self {
179            Self::Function { function, .. } => function.clone(),
180            Self::RawResourceConstructor { resource, .. } => format!("[constructor]{resource}"),
181            Self::RawResourceDrop { resource, .. } => format!("[drop]{resource}"),
182            Self::RawResourceMethod {
183                resource, method, ..
184            } => format!("[method]{resource}.{method}"),
185            Self::RawResourceStaticMethod {
186                resource, method, ..
187            } => format!("[static]{resource}.{method}"),
188        }
189    }
190
191    pub fn resource_method_name(&self) -> Option<String> {
192        match self {
193            Self::RawResourceMethod { method, .. }
194            | Self::RawResourceStaticMethod { method, .. } => Some(method.clone()),
195            _ => None,
196        }
197    }
198
199    pub fn method_as_static(&self) -> Option<ParsedFunctionReference> {
200        match self {
201            Self::RawResourceMethod { resource, method } => Some(Self::RawResourceStaticMethod {
202                resource: resource.clone(),
203                method: method.clone(),
204            }),
205
206            _ => None,
207        }
208    }
209
210    pub fn resource_name(&self) -> Option<&String> {
211        match self {
212            Self::RawResourceConstructor { resource }
213            | Self::RawResourceDrop { resource }
214            | Self::RawResourceMethod { resource, .. }
215            | Self::RawResourceStaticMethod { resource, .. } => Some(resource),
216            _ => None,
217        }
218    }
219}
220
221// DynamicParsedFunctionName is different from ParsedFunctionName.
222// In `DynamicParsedFunctionName` the resource parameters are `Expr` (Rib) while they are `String`
223// in `ParsedFunctionName`.
224// `Expr` implies the real values are yet to be computed, while `String`
225// in ParsedFunctionName is a textual representation of the evaluated values.
226// `Examples`:
227// `DynamicParsedFunctionName` : ns:name/interface.{resource1(identifier1, { field-a: some(identifier2) }).new}
228// `ParsedFunctionName` : ns:name/interface.{resource1("foo", { field-a: some("bar") }).new}
229#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
230pub struct DynamicParsedFunctionName {
231    pub site: ParsedFunctionSite,
232    pub function: DynamicParsedFunctionReference,
233}
234
235impl DynamicParsedFunctionName {
236    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
237        let name = name.as_ref();
238
239        let mut parser = crate::parser::call::function_name();
240
241        let result = parser.easy_parse(Stream::new(name));
242
243        match result {
244            Ok((parsed, _)) => Ok(parsed),
245            Err(error) => {
246                let error_message = error.map_position(|p| p.to_string()).to_string();
247                Err(error_message)
248            }
249        }
250    }
251
252    pub fn function_name_with_prefix_identifiers(&self) -> String {
253        self.to_parsed_function_name().function.function_name()
254    }
255
256    // Usually resource name in the real metadata consist of prefixes such as [constructor]
257    // However, the one obtained through the dynamic-parsed-function-name is simple without these prefix
258    pub fn resource_name_simplified(&self) -> Option<String> {
259        self.to_parsed_function_name()
260            .function
261            .resource_name()
262            .cloned()
263    }
264
265    // Usually resource method in the real metadata consist of prefixes such as [method]
266    pub fn resource_method_name_simplified(&self) -> Option<String> {
267        self.to_parsed_function_name()
268            .function
269            .resource_method_name()
270    }
271
272    //
273    pub fn to_parsed_function_name(&self) -> ParsedFunctionName {
274        ParsedFunctionName {
275            site: self.site.clone(),
276            function: self.function.to_static(),
277        }
278    }
279}
280
281impl Display for DynamicParsedFunctionName {
282    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
283        let function_name = self.to_parsed_function_name().to_string();
284        write!(f, "{function_name}")
285    }
286}
287
288#[derive(Debug, PartialEq, Eq, Clone, Hash)]
289pub struct ParsedFunctionName {
290    pub site: ParsedFunctionSite,
291    pub function: ParsedFunctionReference,
292}
293
294impl Serialize for ParsedFunctionName {
295    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
296        let function_name = self.to_string();
297        serializer.serialize_str(&function_name)
298    }
299}
300
301impl<'de> Deserialize<'de> for ParsedFunctionName {
302    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
303    where
304        D: serde::Deserializer<'de>,
305    {
306        let function_name = <String as Deserialize>::deserialize(deserializer)?;
307        ParsedFunctionName::parse(function_name).map_err(serde::de::Error::custom)
308    }
309}
310
311impl Display for ParsedFunctionName {
312    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
313        let function_name = self
314            .site
315            .interface_name()
316            .map_or(self.function.function_name(), |interface| {
317                format!("{}.{{{}}}", interface, self.function)
318            });
319        write!(f, "{function_name}")
320    }
321}
322
323impl ParsedFunctionName {
324    pub fn new(site: ParsedFunctionSite, function: ParsedFunctionReference) -> Self {
325        Self { site, function }
326    }
327
328    pub fn global(name: String) -> Self {
329        Self {
330            site: ParsedFunctionSite::Global,
331            function: ParsedFunctionReference::Function { function: name },
332        }
333    }
334
335    pub fn on_interface(interface: String, function: String) -> Self {
336        Self {
337            site: ParsedFunctionSite::Interface { name: interface },
338            function: ParsedFunctionReference::Function { function },
339        }
340    }
341
342    pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
343        let name = name.as_ref();
344
345        let mut parser = crate::parser::call::function_name().skip(eof());
346
347        let result = parser.easy_parse(Stream::new(name));
348
349        match result {
350            Ok((parsed, _)) => Ok(parsed.to_parsed_function_name()),
351            Err(error) => {
352                let error_message = error.map_position(|p| p.to_string()).to_string();
353                Err(error_message)
354            }
355        }
356    }
357
358    pub fn site(&self) -> &ParsedFunctionSite {
359        &self.site
360    }
361
362    pub fn function(&self) -> &ParsedFunctionReference {
363        &self.function
364    }
365
366    pub fn method_as_static(&self) -> Option<Self> {
367        self.function.method_as_static().map(|function| Self {
368            site: self.site.clone(),
369            function,
370        })
371    }
372
373    pub fn is_constructor(&self) -> Option<&str> {
374        match &self.function {
375            ParsedFunctionReference::RawResourceConstructor { resource, .. } => Some(resource),
376            _ => None,
377        }
378    }
379
380    pub fn is_method(&self) -> Option<&str> {
381        match &self.function {
382            ParsedFunctionReference::RawResourceMethod { resource, .. }
383            | ParsedFunctionReference::RawResourceStaticMethod { resource, .. } => Some(resource),
384            _ => None,
385        }
386    }
387
388    pub fn is_static_method(&self) -> Option<&str> {
389        match &self.function {
390            ParsedFunctionReference::RawResourceStaticMethod { resource, .. } => Some(resource),
391            _ => None,
392        }
393    }
394
395    pub fn with_site(&self, site: ParsedFunctionSite) -> Self {
396        Self {
397            site,
398            function: self.function.clone(),
399        }
400    }
401}
402
403#[cfg(test)]
404mod function_name_tests {
405    use super::{ParsedFunctionName, ParsedFunctionReference, ParsedFunctionSite, SemVer};
406    use test_r::test;
407
408    #[test]
409    fn parse_function_name_does_not_accept_partial_matches() {
410        let result = ParsedFunctionName::parse("x:y/z");
411        assert!(result.is_err());
412    }
413
414    #[test]
415    fn parse_function_name_global() {
416        let parsed = ParsedFunctionName::parse("run-example").expect("Parsing failed");
417        assert_eq!(parsed.site().interface_name(), None);
418        assert_eq!(parsed.function().function_name(), "run-example");
419        assert_eq!(
420            parsed,
421            ParsedFunctionName {
422                site: ParsedFunctionSite::Global,
423                function: ParsedFunctionReference::Function {
424                    function: "run-example".to_string()
425                },
426            }
427        );
428    }
429
430    #[test]
431    fn parse_function_name_in_exported_interface_no_package() {
432        let parsed = ParsedFunctionName::parse("interface.{fn1}").expect("Parsing failed");
433        assert_eq!(
434            parsed.site().interface_name(),
435            Some("interface".to_string())
436        );
437        assert_eq!(parsed.function().function_name(), "fn1".to_string());
438        assert_eq!(
439            parsed,
440            ParsedFunctionName {
441                site: ParsedFunctionSite::Interface {
442                    name: "interface".to_string()
443                },
444                function: ParsedFunctionReference::Function {
445                    function: "fn1".to_string()
446                },
447            }
448        );
449    }
450
451    #[test]
452    fn parse_function_name_in_exported_interface() {
453        let parsed = ParsedFunctionName::parse("ns:name/interface.{fn1}").expect("Parsing failed");
454        assert_eq!(
455            parsed.site().interface_name(),
456            Some("ns:name/interface".to_string())
457        );
458        assert_eq!(parsed.function().function_name(), "fn1".to_string());
459        assert_eq!(
460            parsed,
461            ParsedFunctionName {
462                site: ParsedFunctionSite::PackagedInterface {
463                    namespace: "ns".to_string(),
464                    package: "name".to_string(),
465                    interface: "interface".to_string(),
466                    version: None,
467                },
468                function: ParsedFunctionReference::Function {
469                    function: "fn1".to_string()
470                },
471            }
472        );
473    }
474
475    #[test]
476    fn parse_function_name_in_versioned_exported_interface() {
477        let parsed = ParsedFunctionName::parse("wasi:cli/run@0.2.0.{run}").expect("Parsing failed");
478        assert_eq!(
479            parsed.site().interface_name(),
480            Some("wasi:cli/run@0.2.0".to_string())
481        );
482        assert_eq!(parsed.function().function_name(), "run".to_string());
483        assert_eq!(
484            parsed,
485            ParsedFunctionName {
486                site: ParsedFunctionSite::PackagedInterface {
487                    namespace: "wasi".to_string(),
488                    package: "cli".to_string(),
489                    interface: "run".to_string(),
490                    version: Some(SemVer(semver::Version::new(0, 2, 0))),
491                },
492                function: ParsedFunctionReference::Function {
493                    function: "run".to_string()
494                },
495            }
496        );
497    }
498
499    #[test]
500    fn parse_function_name_constructor_syntax_sugar() {
501        let parsed =
502            ParsedFunctionName::parse("ns:name/interface.{resource1.new}").expect("Parsing failed");
503        assert_eq!(
504            parsed.site().interface_name(),
505            Some("ns:name/interface".to_string())
506        );
507        assert_eq!(
508            parsed.function().function_name(),
509            "[constructor]resource1".to_string()
510        );
511        assert_eq!(
512            parsed,
513            ParsedFunctionName {
514                site: ParsedFunctionSite::PackagedInterface {
515                    namespace: "ns".to_string(),
516                    package: "name".to_string(),
517                    interface: "interface".to_string(),
518                    version: None,
519                },
520                function: ParsedFunctionReference::RawResourceConstructor {
521                    resource: "resource1".to_string()
522                },
523            }
524        );
525    }
526
527    #[test]
528    fn parse_function_name_constructor() {
529        let parsed = ParsedFunctionName::parse("ns:name/interface.{[constructor]resource1}")
530            .expect("Parsing failed");
531        assert_eq!(
532            parsed.site().interface_name(),
533            Some("ns:name/interface".to_string())
534        );
535        assert_eq!(
536            parsed.function().function_name(),
537            "[constructor]resource1".to_string()
538        );
539        assert_eq!(
540            parsed,
541            ParsedFunctionName {
542                site: ParsedFunctionSite::PackagedInterface {
543                    namespace: "ns".to_string(),
544                    package: "name".to_string(),
545                    interface: "interface".to_string(),
546                    version: None,
547                },
548                function: ParsedFunctionReference::RawResourceConstructor {
549                    resource: "resource1".to_string()
550                },
551            }
552        );
553    }
554
555    #[test]
556    fn parse_function_name_method_syntax_sugar() {
557        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something}")
558            .expect("Parsing failed");
559        assert_eq!(
560            parsed.site().interface_name(),
561            Some("ns:name/interface".to_string())
562        );
563        assert_eq!(
564            parsed.function().function_name(),
565            "[method]resource1.do-something".to_string()
566        );
567        assert_eq!(
568            parsed,
569            ParsedFunctionName {
570                site: ParsedFunctionSite::PackagedInterface {
571                    namespace: "ns".to_string(),
572                    package: "name".to_string(),
573                    interface: "interface".to_string(),
574                    version: None,
575                },
576                function: ParsedFunctionReference::RawResourceMethod {
577                    resource: "resource1".to_string(),
578                    method: "do-something".to_string(),
579                },
580            }
581        );
582    }
583
584    #[test]
585    fn parse_function_name_method() {
586        let parsed =
587            ParsedFunctionName::parse("ns:name/interface.{[method]resource1.do-something}")
588                .expect("Parsing failed");
589        assert_eq!(
590            parsed.site().interface_name(),
591            Some("ns:name/interface".to_string())
592        );
593        assert_eq!(
594            parsed.function().function_name(),
595            "[method]resource1.do-something".to_string()
596        );
597        assert_eq!(
598            parsed,
599            ParsedFunctionName {
600                site: ParsedFunctionSite::PackagedInterface {
601                    namespace: "ns".to_string(),
602                    package: "name".to_string(),
603                    interface: "interface".to_string(),
604                    version: None,
605                },
606                function: ParsedFunctionReference::RawResourceMethod {
607                    resource: "resource1".to_string(),
608                    method: "do-something".to_string(),
609                },
610            }
611        );
612    }
613
614    #[test]
615    fn parse_function_name_static_method_syntax_sugar() {
616        // Note: the syntax sugared version cannot distinguish between method and static - so we need to check the actual existence of
617        // the function and fallback.
618        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something-static}")
619            .expect("Parsing failed")
620            .method_as_static()
621            .unwrap();
622        assert_eq!(
623            parsed.site().interface_name(),
624            Some("ns:name/interface".to_string())
625        );
626        assert_eq!(
627            parsed.function().function_name(),
628            "[static]resource1.do-something-static".to_string()
629        );
630        assert_eq!(
631            parsed,
632            ParsedFunctionName {
633                site: ParsedFunctionSite::PackagedInterface {
634                    namespace: "ns".to_string(),
635                    package: "name".to_string(),
636                    interface: "interface".to_string(),
637                    version: None,
638                },
639                function: ParsedFunctionReference::RawResourceStaticMethod {
640                    resource: "resource1".to_string(),
641                    method: "do-something-static".to_string(),
642                },
643            }
644        );
645    }
646
647    #[test]
648    fn parse_function_name_static() {
649        let parsed =
650            ParsedFunctionName::parse("ns:name/interface.{[static]resource1.do-something-static}")
651                .expect("Parsing failed");
652        assert_eq!(
653            parsed.site().interface_name(),
654            Some("ns:name/interface".to_string())
655        );
656        assert_eq!(
657            parsed.function().function_name(),
658            "[static]resource1.do-something-static".to_string()
659        );
660        assert_eq!(
661            parsed,
662            ParsedFunctionName {
663                site: ParsedFunctionSite::PackagedInterface {
664                    namespace: "ns".to_string(),
665                    package: "name".to_string(),
666                    interface: "interface".to_string(),
667                    version: None,
668                },
669                function: ParsedFunctionReference::RawResourceStaticMethod {
670                    resource: "resource1".to_string(),
671                    method: "do-something-static".to_string(),
672                },
673            }
674        );
675    }
676
677    #[test]
678    fn parse_function_name_drop_syntax_sugar() {
679        let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.drop}")
680            .expect("Parsing failed");
681        assert_eq!(
682            parsed.site().interface_name(),
683            Some("ns:name/interface".to_string())
684        );
685        assert_eq!(
686            parsed.function().function_name(),
687            "[drop]resource1".to_string()
688        );
689        assert_eq!(
690            parsed,
691            ParsedFunctionName {
692                site: ParsedFunctionSite::PackagedInterface {
693                    namespace: "ns".to_string(),
694                    package: "name".to_string(),
695                    interface: "interface".to_string(),
696                    version: None,
697                },
698                function: ParsedFunctionReference::RawResourceDrop {
699                    resource: "resource1".to_string()
700                },
701            }
702        );
703    }
704
705    #[test]
706    fn parse_function_name_drop() {
707        let parsed = ParsedFunctionName::parse("ns:name/interface.{[drop]resource1}")
708            .expect("Parsing failed");
709        assert_eq!(
710            parsed.site().interface_name(),
711            Some("ns:name/interface".to_string())
712        );
713        assert_eq!(
714            parsed.function().function_name(),
715            "[drop]resource1".to_string()
716        );
717        assert_eq!(
718            parsed,
719            ParsedFunctionName {
720                site: ParsedFunctionSite::PackagedInterface {
721                    namespace: "ns".to_string(),
722                    package: "name".to_string(),
723                    interface: "interface".to_string(),
724                    version: None,
725                },
726                function: ParsedFunctionReference::RawResourceDrop {
727                    resource: "resource1".to_string()
728                },
729            }
730        );
731    }
732
733    fn round_trip_function_name_parse(input: &str) {
734        let parsed = ParsedFunctionName::parse(input)
735            .unwrap_or_else(|_| panic!("Input Parsing failed for {input}"));
736        let parsed_written =
737            ParsedFunctionName::parse(parsed.to_string()).expect("Round-trip parsing failed");
738        assert_eq!(parsed, parsed_written);
739    }
740
741    #[test]
742    fn test_parsed_function_name_display() {
743        round_trip_function_name_parse("run-example");
744        round_trip_function_name_parse("interface.{fn1}");
745        round_trip_function_name_parse("wasi:cli/run@0.2.0.{run}");
746        round_trip_function_name_parse("ns:name/interface.{resource1.new}");
747        round_trip_function_name_parse("ns:name/interface.{[constructor]resource1}");
748        round_trip_function_name_parse("ns:name/interface.{resource1.do-something}");
749        round_trip_function_name_parse("ns:name/interface.{[static]resource1.do-something-static}");
750        round_trip_function_name_parse("ns:name/interface.{resource1.drop}");
751        round_trip_function_name_parse("ns:name/interface.{[drop]resource1}");
752    }
753}