1use bincode::{BorrowDecode, Decode, Encode};
16use combine::stream::position::Stream;
17use combine::{eof, EasyParser, Parser};
18use semver::{BuildMetadata, Prerelease};
19use serde::{Deserialize, Serialize};
20use std::borrow::Cow;
21use std::fmt::Display;
22
23#[derive(PartialEq, Hash, Eq, Clone, Ord, PartialOrd)]
24pub struct SemVer(pub semver::Version);
25
26impl SemVer {
27 pub fn parse(version: &str) -> Result<Self, String> {
28 semver::Version::parse(version)
29 .map(SemVer)
30 .map_err(|e| format!("Invalid semver string: {e}"))
31 }
32}
33
34impl std::fmt::Debug for SemVer {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(f, "{}", self.0)
37 }
38}
39
40impl Encode for SemVer {
41 fn encode<E: bincode::enc::Encoder>(
42 &self,
43 encoder: &mut E,
44 ) -> Result<(), bincode::error::EncodeError> {
45 self.0.major.encode(encoder)?;
46 self.0.minor.encode(encoder)?;
47 self.0.patch.encode(encoder)?;
48 self.0.pre.as_str().encode(encoder)?;
49 self.0.build.as_str().encode(encoder)?;
50 Ok(())
51 }
52}
53
54impl<Context> Decode<Context> for SemVer {
55 fn decode<D: bincode::de::Decoder<Context = Context>>(
56 decoder: &mut D,
57 ) -> Result<Self, bincode::error::DecodeError> {
58 let major = u64::decode(decoder)?;
59 let minor = u64::decode(decoder)?;
60 let patch = u64::decode(decoder)?;
61 let pre_str = String::decode(decoder)?;
62 let build_str = String::decode(decoder)?;
63 let pre = Prerelease::new(&pre_str)
64 .map_err(|_| bincode::error::DecodeError::OtherString("Invalid prerelease".into()))?;
65 let build = BuildMetadata::new(&build_str).map_err(|_| {
66 bincode::error::DecodeError::OtherString("Invalid build metadata".into())
67 })?;
68
69 Ok(SemVer(semver::Version {
70 major,
71 minor,
72 patch,
73 pre,
74 build,
75 }))
76 }
77}
78
79impl<'de, Context> BorrowDecode<'de, Context> for SemVer {
80 fn borrow_decode<D: bincode::de::BorrowDecoder<'de, Context = Context>>(
81 decoder: &mut D,
82 ) -> Result<Self, bincode::error::DecodeError> {
83 let major = u64::borrow_decode(decoder)?;
84 let minor = u64::borrow_decode(decoder)?;
85 let patch = u64::borrow_decode(decoder)?;
86 let pre_str = <Cow<'de, str> as BorrowDecode<Context>>::borrow_decode(decoder)?;
87 let build_str = <Cow<'de, str> as BorrowDecode<Context>>::borrow_decode(decoder)?;
88 let pre = Prerelease::new(&pre_str)
89 .map_err(|_| bincode::error::DecodeError::OtherString("Invalid prerelease".into()))?;
90 let build = BuildMetadata::new(&build_str).map_err(|_| {
91 bincode::error::DecodeError::OtherString("Invalid build metadata".into())
92 })?;
93 Ok(SemVer(semver::Version {
94 major,
95 minor,
96 patch,
97 pre,
98 build,
99 }))
100 }
101}
102
103#[derive(Debug, Hash, PartialEq, Eq, Clone, Encode, Decode, Ord, PartialOrd)]
104pub enum ParsedFunctionSite {
105 Global,
106 Interface {
107 name: String,
108 },
109 PackagedInterface {
110 namespace: String,
111 package: String,
112 interface: String,
113 version: Option<SemVer>,
114 },
115}
116
117impl ParsedFunctionSite {
118 pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
119 ParsedFunctionName::parse(format!("{}.{{x}}", name.as_ref()))
120 .map(|ParsedFunctionName { site, .. }| site)
121 }
122
123 pub fn interface_name(&self) -> Option<String> {
124 match self {
125 Self::Global => None,
126 Self::Interface { name } => Some(name.clone()),
127 Self::PackagedInterface {
128 namespace,
129 package,
130 interface,
131 version: None,
132 } => Some(format!("{namespace}:{package}/{interface}")),
133 Self::PackagedInterface {
134 namespace,
135 package,
136 interface,
137 version: Some(version),
138 } => Some(format!("{namespace}:{package}/{interface}@{}", version.0)),
139 }
140 }
141
142 pub fn unversioned(&self) -> ParsedFunctionSite {
143 match self {
144 ParsedFunctionSite::Global => ParsedFunctionSite::Global,
145 ParsedFunctionSite::Interface { name } => {
146 ParsedFunctionSite::Interface { name: name.clone() }
147 }
148 ParsedFunctionSite::PackagedInterface {
149 namespace,
150 package,
151 interface,
152 version: _,
153 } => ParsedFunctionSite::PackagedInterface {
154 namespace: namespace.clone(),
155 package: package.clone(),
156 interface: interface.clone(),
157 version: None,
158 },
159 }
160 }
161}
162
163#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
164pub enum DynamicParsedFunctionReference {
165 Function { function: String },
166 RawResourceConstructor { resource: String },
167 RawResourceDrop { resource: String },
168 RawResourceMethod { resource: String, method: String },
169 RawResourceStaticMethod { resource: String, method: String },
170}
171
172impl DynamicParsedFunctionReference {
173 pub fn name_pretty(&self) -> String {
174 match self {
175 DynamicParsedFunctionReference::Function { function, .. } => function.clone(),
176 DynamicParsedFunctionReference::RawResourceConstructor { resource, .. } => {
177 resource.to_string()
178 }
179 DynamicParsedFunctionReference::RawResourceDrop { .. } => "drop".to_string(),
180 DynamicParsedFunctionReference::RawResourceMethod { method, .. } => method.to_string(),
181 DynamicParsedFunctionReference::RawResourceStaticMethod { method, .. } => {
182 method.to_string()
183 }
184 }
185 }
186
187 fn to_static(&self) -> ParsedFunctionReference {
188 match self {
189 Self::Function { function } => ParsedFunctionReference::Function {
190 function: function.clone(),
191 },
192 Self::RawResourceConstructor { resource } => {
193 ParsedFunctionReference::RawResourceConstructor {
194 resource: resource.clone(),
195 }
196 }
197 Self::RawResourceDrop { resource } => ParsedFunctionReference::RawResourceDrop {
198 resource: resource.clone(),
199 },
200 Self::RawResourceMethod { resource, method } => {
201 ParsedFunctionReference::RawResourceMethod {
202 resource: resource.clone(),
203 method: method.clone(),
204 }
205 }
206 Self::RawResourceStaticMethod { resource, method } => {
207 ParsedFunctionReference::RawResourceStaticMethod {
208 resource: resource.clone(),
209 method: method.clone(),
210 }
211 }
212 }
213 }
214}
215
216#[derive(Debug, PartialEq, Eq, Clone, Hash, Encode, Decode)]
217pub enum ParsedFunctionReference {
218 Function { function: String },
219 RawResourceConstructor { resource: String },
220 RawResourceDrop { resource: String },
221 RawResourceMethod { resource: String, method: String },
222 RawResourceStaticMethod { resource: String, method: String },
223}
224
225impl Display for ParsedFunctionReference {
226 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227 let function_name = match self {
228 Self::Function { function } => function.clone(),
229 Self::RawResourceConstructor { resource } => format!("{resource}.new"),
230 Self::RawResourceMethod { resource, method } => format!("{resource}.{method}"),
231 Self::RawResourceStaticMethod { resource, method } => {
232 format!("[static]{resource}.{method}")
233 }
234 Self::RawResourceDrop { resource } => format!("{resource}.drop"),
235 };
236
237 write!(f, "{function_name}")
238 }
239}
240
241impl ParsedFunctionReference {
242 pub fn function_name(&self) -> String {
243 match self {
244 Self::Function { function, .. } => function.clone(),
245 Self::RawResourceConstructor { resource, .. } => format!("[constructor]{resource}"),
246 Self::RawResourceDrop { resource, .. } => format!("[drop]{resource}"),
247 Self::RawResourceMethod {
248 resource, method, ..
249 } => format!("[method]{resource}.{method}"),
250 Self::RawResourceStaticMethod {
251 resource, method, ..
252 } => format!("[static]{resource}.{method}"),
253 }
254 }
255
256 pub fn resource_method_name(&self) -> Option<String> {
257 match self {
258 Self::RawResourceMethod { method, .. }
259 | Self::RawResourceStaticMethod { method, .. } => Some(method.clone()),
260 _ => None,
261 }
262 }
263
264 pub fn method_as_static(&self) -> Option<ParsedFunctionReference> {
265 match self {
266 Self::RawResourceMethod { resource, method } => Some(Self::RawResourceStaticMethod {
267 resource: resource.clone(),
268 method: method.clone(),
269 }),
270
271 _ => None,
272 }
273 }
274
275 pub fn resource_name(&self) -> Option<&String> {
276 match self {
277 Self::RawResourceConstructor { resource }
278 | Self::RawResourceDrop { resource }
279 | Self::RawResourceMethod { resource, .. }
280 | Self::RawResourceStaticMethod { resource, .. } => Some(resource),
281 _ => None,
282 }
283 }
284}
285
286#[derive(Debug, Hash, PartialEq, Eq, Clone, Ord, PartialOrd)]
295pub struct DynamicParsedFunctionName {
296 pub site: ParsedFunctionSite,
297 pub function: DynamicParsedFunctionReference,
298}
299
300impl DynamicParsedFunctionName {
301 pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
302 let name = name.as_ref();
303
304 let mut parser = crate::parser::call::function_name();
305
306 let result = parser.easy_parse(Stream::new(name));
307
308 match result {
309 Ok((parsed, _)) => Ok(parsed),
310 Err(error) => {
311 let error_message = error.map_position(|p| p.to_string()).to_string();
312 Err(error_message)
313 }
314 }
315 }
316
317 pub fn function_name_with_prefix_identifiers(&self) -> String {
318 self.to_parsed_function_name().function.function_name()
319 }
320
321 pub fn resource_name_simplified(&self) -> Option<String> {
324 self.to_parsed_function_name()
325 .function
326 .resource_name()
327 .cloned()
328 }
329
330 pub fn resource_method_name_simplified(&self) -> Option<String> {
332 self.to_parsed_function_name()
333 .function
334 .resource_method_name()
335 }
336
337 pub fn to_parsed_function_name(&self) -> ParsedFunctionName {
339 ParsedFunctionName {
340 site: self.site.clone(),
341 function: self.function.to_static(),
342 }
343 }
344}
345
346impl Display for DynamicParsedFunctionName {
347 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
348 let function_name = self.to_parsed_function_name().to_string();
349 write!(f, "{function_name}")
350 }
351}
352
353#[derive(Debug, PartialEq, Eq, Clone, Hash, Encode, Decode)]
354pub struct ParsedFunctionName {
355 pub site: ParsedFunctionSite,
356 pub function: ParsedFunctionReference,
357}
358
359impl Serialize for ParsedFunctionName {
360 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
361 let function_name = self.to_string();
362 serializer.serialize_str(&function_name)
363 }
364}
365
366impl<'de> Deserialize<'de> for ParsedFunctionName {
367 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
368 where
369 D: serde::Deserializer<'de>,
370 {
371 let function_name = String::deserialize(deserializer)?;
372 ParsedFunctionName::parse(function_name).map_err(serde::de::Error::custom)
373 }
374}
375
376impl Display for ParsedFunctionName {
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 let function_name = self
379 .site
380 .interface_name()
381 .map_or(self.function.function_name(), |interface| {
382 format!("{}.{{{}}}", interface, self.function)
383 });
384 write!(f, "{function_name}")
385 }
386}
387
388impl ParsedFunctionName {
389 pub fn new(site: ParsedFunctionSite, function: ParsedFunctionReference) -> Self {
390 Self { site, function }
391 }
392
393 pub fn global(name: String) -> Self {
394 Self {
395 site: ParsedFunctionSite::Global,
396 function: ParsedFunctionReference::Function { function: name },
397 }
398 }
399
400 pub fn on_interface(interface: String, function: String) -> Self {
401 Self {
402 site: ParsedFunctionSite::Interface { name: interface },
403 function: ParsedFunctionReference::Function { function },
404 }
405 }
406
407 pub fn parse(name: impl AsRef<str>) -> Result<Self, String> {
408 let name = name.as_ref();
409
410 let mut parser = crate::parser::call::function_name().skip(eof());
411
412 let result = parser.easy_parse(Stream::new(name));
413
414 match result {
415 Ok((parsed, _)) => Ok(parsed.to_parsed_function_name()),
416 Err(error) => {
417 let error_message = error.map_position(|p| p.to_string()).to_string();
418 Err(error_message)
419 }
420 }
421 }
422
423 pub fn site(&self) -> &ParsedFunctionSite {
424 &self.site
425 }
426
427 pub fn function(&self) -> &ParsedFunctionReference {
428 &self.function
429 }
430
431 pub fn method_as_static(&self) -> Option<Self> {
432 self.function.method_as_static().map(|function| Self {
433 site: self.site.clone(),
434 function,
435 })
436 }
437
438 pub fn is_constructor(&self) -> Option<&str> {
439 match &self.function {
440 ParsedFunctionReference::RawResourceConstructor { resource, .. } => Some(resource),
441 _ => None,
442 }
443 }
444
445 pub fn is_method(&self) -> Option<&str> {
446 match &self.function {
447 ParsedFunctionReference::RawResourceMethod { resource, .. }
448 | ParsedFunctionReference::RawResourceStaticMethod { resource, .. } => Some(resource),
449 _ => None,
450 }
451 }
452
453 pub fn is_static_method(&self) -> Option<&str> {
454 match &self.function {
455 ParsedFunctionReference::RawResourceStaticMethod { resource, .. } => Some(resource),
456 _ => None,
457 }
458 }
459
460 pub fn with_site(&self, site: ParsedFunctionSite) -> Self {
461 Self {
462 site,
463 function: self.function.clone(),
464 }
465 }
466}
467
468#[cfg(feature = "protobuf")]
469mod protobuf {
470 use crate::{
471 DynamicParsedFunctionName, DynamicParsedFunctionReference, ParsedFunctionName,
472 ParsedFunctionReference, ParsedFunctionSite, SemVer,
473 };
474 use golem_api_grpc::proto::golem::rib::dynamic_parsed_function_reference::FunctionReference as ProtoDynamicFunctionReference;
475 use semver::{BuildMetadata, Prerelease};
476
477 impl TryFrom<golem_api_grpc::proto::golem::rib::SemVersion> for SemVer {
478 type Error = String;
479
480 fn try_from(
481 value: golem_api_grpc::proto::golem::rib::SemVersion,
482 ) -> Result<Self, Self::Error> {
483 Ok(SemVer(semver::Version {
484 major: value.major,
485 minor: value.minor,
486 patch: value.patch,
487 pre: Prerelease::new(&value.pre).map_err(|_| "Invalid prerelease".to_string())?,
488 build: BuildMetadata::new(&value.build)
489 .map_err(|_| "Invalid build metadata".to_string())?,
490 }))
491 }
492 }
493
494 impl From<SemVer> for golem_api_grpc::proto::golem::rib::SemVersion {
495 fn from(value: SemVer) -> Self {
496 golem_api_grpc::proto::golem::rib::SemVersion {
497 major: value.0.major,
498 minor: value.0.minor,
499 patch: value.0.patch,
500 pre: value.0.pre.to_string(),
501 build: value.0.build.to_string(),
502 }
503 }
504 }
505
506 impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionSite> for ParsedFunctionSite {
507 type Error = String;
508
509 fn try_from(
510 value: golem_api_grpc::proto::golem::rib::ParsedFunctionSite,
511 ) -> Result<Self, Self::Error> {
512 let site = value.site.ok_or("Missing site".to_string())?;
513 match site {
514 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Global(_) => {
515 Ok(Self::Global)
516 }
517 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Interface(
518 golem_api_grpc::proto::golem::rib::InterfaceFunctionSite { name },
519 ) => Ok(Self::Interface { name }),
520 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::PackageInterface(
521 golem_api_grpc::proto::golem::rib::PackageInterfaceFunctionSite {
522 namespace,
523 package,
524 interface,
525 version,
526 },
527 ) => {
528 let version = match version {
529 Some(version) => Some(version.try_into()?),
530 None => None,
531 };
532
533 Ok(Self::PackagedInterface {
534 namespace,
535 package,
536 interface,
537 version,
538 })
539 }
540 }
541 }
542 }
543
544 impl From<ParsedFunctionSite> for golem_api_grpc::proto::golem::rib::ParsedFunctionSite {
545 fn from(value: ParsedFunctionSite) -> Self {
546 let site = match value {
547 ParsedFunctionSite::Global => {
548 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Global(
549 golem_api_grpc::proto::golem::rib::GlobalFunctionSite {},
550 )
551 }
552 ParsedFunctionSite::Interface { name } => {
553 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::Interface(
554 golem_api_grpc::proto::golem::rib::InterfaceFunctionSite { name },
555 )
556 }
557 ParsedFunctionSite::PackagedInterface {
558 namespace,
559 package,
560 interface,
561 version,
562 } => {
563 golem_api_grpc::proto::golem::rib::parsed_function_site::Site::PackageInterface(
564 golem_api_grpc::proto::golem::rib::PackageInterfaceFunctionSite {
565 namespace,
566 package,
567 interface,
568 version: version.map(|v| v.into()),
569 },
570 )
571 }
572 };
573 golem_api_grpc::proto::golem::rib::ParsedFunctionSite { site: Some(site) }
574 }
575 }
576
577 impl From<DynamicParsedFunctionReference>
578 for golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference
579 {
580 fn from(value: DynamicParsedFunctionReference) -> Self {
581 let function = match value {
582 DynamicParsedFunctionReference::Function { function } => ProtoDynamicFunctionReference::Function(
583 golem_api_grpc::proto::golem::rib::FunctionFunctionReference { function },
584 ),
585 DynamicParsedFunctionReference::RawResourceConstructor { resource } => ProtoDynamicFunctionReference::RawResourceConstructor(
586 golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference { resource },
587 ),
588 DynamicParsedFunctionReference::RawResourceMethod { resource, method } => ProtoDynamicFunctionReference::RawResourceMethod(
589 golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference { resource, method },
590 ),
591 DynamicParsedFunctionReference::RawResourceStaticMethod { resource, method } => ProtoDynamicFunctionReference::RawResourceStaticMethod(
592 golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference { resource, method },
593 ),
594 DynamicParsedFunctionReference::RawResourceDrop { resource } => ProtoDynamicFunctionReference::RawResourceDrop(
595 golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference { resource },
596 ),
597 };
598
599 golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference {
600 function_reference: Some(function),
601 }
602 }
603 }
604
605 impl TryFrom<golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference>
606 for DynamicParsedFunctionReference
607 {
608 type Error = String;
609
610 fn try_from(
611 value: golem_api_grpc::proto::golem::rib::DynamicParsedFunctionReference,
612 ) -> Result<Self, Self::Error> {
613 let function = value
614 .function_reference
615 .ok_or("Missing function reference".to_string())?;
616
617 match function {
618 ProtoDynamicFunctionReference::Function(
619 golem_api_grpc::proto::golem::rib::FunctionFunctionReference { function },
620 ) => Ok(Self::Function { function }),
621 ProtoDynamicFunctionReference::RawResourceConstructor(
622 golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
623 resource,
624 },
625 ) => Ok(Self::RawResourceConstructor { resource }),
626 ProtoDynamicFunctionReference::RawResourceMethod(
627 golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
628 resource,
629 method,
630 },
631 ) => Ok(Self::RawResourceMethod { resource, method }),
632 ProtoDynamicFunctionReference::RawResourceStaticMethod(
633 golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
634 resource,
635 method,
636 },
637 ) => Ok(Self::RawResourceStaticMethod { resource, method }),
638 ProtoDynamicFunctionReference::RawResourceDrop(
639 golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference {
640 resource,
641 },
642 ) => Ok(Self::RawResourceDrop { resource }),
643 }
644 }
645 }
646
647 impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionReference>
648 for ParsedFunctionReference
649 {
650 type Error = String;
651
652 fn try_from(
653 value: golem_api_grpc::proto::golem::rib::ParsedFunctionReference,
654 ) -> Result<Self, Self::Error> {
655 let function = value
656 .function_reference
657 .ok_or("Missing function".to_string())?;
658 match function {
659 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::Function(golem_api_grpc::proto::golem::rib::FunctionFunctionReference {
660 function
661 }) => {
662 Ok(Self::Function { function })
663 }
664 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceConstructor(golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
665 resource
666 }) => {
667 Ok(Self::RawResourceConstructor { resource })
668 }
669 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceMethod(golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
670 resource,
671 method
672 }) => {
673 Ok(Self::RawResourceMethod { resource, method })
674 }
675 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceStaticMethod(golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
676 resource,
677 method
678 }) => {
679 Ok(Self::RawResourceStaticMethod { resource, method })
680 }
681 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceDrop(golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference {
682 resource
683 }) => {
684 Ok(Self::RawResourceDrop { resource })
685 }
686 }
687 }
688 }
689
690 impl From<ParsedFunctionReference> for golem_api_grpc::proto::golem::rib::ParsedFunctionReference {
691 fn from(value: ParsedFunctionReference) -> Self {
692 let function = match value {
693 ParsedFunctionReference::Function { function } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::Function(
694 golem_api_grpc::proto::golem::rib::FunctionFunctionReference { function },
695 ),
696 ParsedFunctionReference::RawResourceConstructor { resource } => {
697 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceConstructor(
698 golem_api_grpc::proto::golem::rib::RawResourceConstructorFunctionReference {
699 resource,
700 },
701 )
702 }
703 ParsedFunctionReference::RawResourceMethod { resource, method } => {
704 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceMethod(
705 golem_api_grpc::proto::golem::rib::RawResourceMethodFunctionReference {
706 resource,
707 method,
708 },
709 )
710 }
711 ParsedFunctionReference::RawResourceStaticMethod { resource, method } => {
712 golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceStaticMethod(
713 golem_api_grpc::proto::golem::rib::RawResourceStaticMethodFunctionReference {
714 resource,
715 method,
716 },
717 )
718 }
719 ParsedFunctionReference::RawResourceDrop { resource } => golem_api_grpc::proto::golem::rib::parsed_function_reference::FunctionReference::RawResourceDrop(
720 golem_api_grpc::proto::golem::rib::RawResourceDropFunctionReference { resource },
721 ),
722 };
723 golem_api_grpc::proto::golem::rib::ParsedFunctionReference {
724 function_reference: Some(function),
725 }
726 }
727 }
728
729 impl TryFrom<golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName>
730 for DynamicParsedFunctionName
731 {
732 type Error = String;
733
734 fn try_from(
735 value: golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName,
736 ) -> Result<Self, Self::Error> {
737 let site = ParsedFunctionSite::try_from(value.site.ok_or("Missing site".to_string())?)?;
738 let function = DynamicParsedFunctionReference::try_from(
739 value.function.ok_or("Missing function".to_string())?,
740 )?;
741 Ok(Self { site, function })
742 }
743 }
744
745 impl TryFrom<golem_api_grpc::proto::golem::rib::ParsedFunctionName> for ParsedFunctionName {
746 type Error = String;
747
748 fn try_from(
749 value: golem_api_grpc::proto::golem::rib::ParsedFunctionName,
750 ) -> Result<Self, Self::Error> {
751 let site = ParsedFunctionSite::try_from(value.site.ok_or("Missing site".to_string())?)?;
752 let function = ParsedFunctionReference::try_from(
753 value.function.ok_or("Missing function".to_string())?,
754 )?;
755 Ok(Self { site, function })
756 }
757 }
758
759 impl From<ParsedFunctionName> for golem_api_grpc::proto::golem::rib::ParsedFunctionName {
760 fn from(value: ParsedFunctionName) -> Self {
761 golem_api_grpc::proto::golem::rib::ParsedFunctionName {
762 site: Some(value.site.into()),
763 function: Some(value.function.into()),
764 }
765 }
766 }
767
768 impl From<DynamicParsedFunctionName>
769 for golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName
770 {
771 fn from(value: DynamicParsedFunctionName) -> Self {
772 golem_api_grpc::proto::golem::rib::DynamicParsedFunctionName {
773 site: Some(value.site.into()),
774 function: Some(value.function.into()),
775 }
776 }
777 }
778}
779
780#[cfg(test)]
781mod function_name_tests {
782 use super::{ParsedFunctionName, ParsedFunctionReference, ParsedFunctionSite, SemVer};
783 use test_r::test;
784
785 #[test]
786 fn parse_function_name_does_not_accept_partial_matches() {
787 let result = ParsedFunctionName::parse("x:y/z");
788 assert!(result.is_err());
789 }
790
791 #[test]
792 fn parse_function_name_global() {
793 let parsed = ParsedFunctionName::parse("run-example").expect("Parsing failed");
794 assert_eq!(parsed.site().interface_name(), None);
795 assert_eq!(parsed.function().function_name(), "run-example");
796 assert_eq!(
797 parsed,
798 ParsedFunctionName {
799 site: ParsedFunctionSite::Global,
800 function: ParsedFunctionReference::Function {
801 function: "run-example".to_string()
802 },
803 }
804 );
805 }
806
807 #[test]
808 fn parse_function_name_in_exported_interface_no_package() {
809 let parsed = ParsedFunctionName::parse("interface.{fn1}").expect("Parsing failed");
810 assert_eq!(
811 parsed.site().interface_name(),
812 Some("interface".to_string())
813 );
814 assert_eq!(parsed.function().function_name(), "fn1".to_string());
815 assert_eq!(
816 parsed,
817 ParsedFunctionName {
818 site: ParsedFunctionSite::Interface {
819 name: "interface".to_string()
820 },
821 function: ParsedFunctionReference::Function {
822 function: "fn1".to_string()
823 },
824 }
825 );
826 }
827
828 #[test]
829 fn parse_function_name_in_exported_interface() {
830 let parsed = ParsedFunctionName::parse("ns:name/interface.{fn1}").expect("Parsing failed");
831 assert_eq!(
832 parsed.site().interface_name(),
833 Some("ns:name/interface".to_string())
834 );
835 assert_eq!(parsed.function().function_name(), "fn1".to_string());
836 assert_eq!(
837 parsed,
838 ParsedFunctionName {
839 site: ParsedFunctionSite::PackagedInterface {
840 namespace: "ns".to_string(),
841 package: "name".to_string(),
842 interface: "interface".to_string(),
843 version: None,
844 },
845 function: ParsedFunctionReference::Function {
846 function: "fn1".to_string()
847 },
848 }
849 );
850 }
851
852 #[test]
853 fn parse_function_name_in_versioned_exported_interface() {
854 let parsed = ParsedFunctionName::parse("wasi:cli/run@0.2.0.{run}").expect("Parsing failed");
855 assert_eq!(
856 parsed.site().interface_name(),
857 Some("wasi:cli/run@0.2.0".to_string())
858 );
859 assert_eq!(parsed.function().function_name(), "run".to_string());
860 assert_eq!(
861 parsed,
862 ParsedFunctionName {
863 site: ParsedFunctionSite::PackagedInterface {
864 namespace: "wasi".to_string(),
865 package: "cli".to_string(),
866 interface: "run".to_string(),
867 version: Some(SemVer(semver::Version::new(0, 2, 0))),
868 },
869 function: ParsedFunctionReference::Function {
870 function: "run".to_string()
871 },
872 }
873 );
874 }
875
876 #[test]
877 fn parse_function_name_constructor_syntax_sugar() {
878 let parsed =
879 ParsedFunctionName::parse("ns:name/interface.{resource1.new}").expect("Parsing failed");
880 assert_eq!(
881 parsed.site().interface_name(),
882 Some("ns:name/interface".to_string())
883 );
884 assert_eq!(
885 parsed.function().function_name(),
886 "[constructor]resource1".to_string()
887 );
888 assert_eq!(
889 parsed,
890 ParsedFunctionName {
891 site: ParsedFunctionSite::PackagedInterface {
892 namespace: "ns".to_string(),
893 package: "name".to_string(),
894 interface: "interface".to_string(),
895 version: None,
896 },
897 function: ParsedFunctionReference::RawResourceConstructor {
898 resource: "resource1".to_string()
899 },
900 }
901 );
902 }
903
904 #[test]
905 fn parse_function_name_constructor() {
906 let parsed = ParsedFunctionName::parse("ns:name/interface.{[constructor]resource1}")
907 .expect("Parsing failed");
908 assert_eq!(
909 parsed.site().interface_name(),
910 Some("ns:name/interface".to_string())
911 );
912 assert_eq!(
913 parsed.function().function_name(),
914 "[constructor]resource1".to_string()
915 );
916 assert_eq!(
917 parsed,
918 ParsedFunctionName {
919 site: ParsedFunctionSite::PackagedInterface {
920 namespace: "ns".to_string(),
921 package: "name".to_string(),
922 interface: "interface".to_string(),
923 version: None,
924 },
925 function: ParsedFunctionReference::RawResourceConstructor {
926 resource: "resource1".to_string()
927 },
928 }
929 );
930 }
931
932 #[test]
933 fn parse_function_name_method_syntax_sugar() {
934 let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something}")
935 .expect("Parsing failed");
936 assert_eq!(
937 parsed.site().interface_name(),
938 Some("ns:name/interface".to_string())
939 );
940 assert_eq!(
941 parsed.function().function_name(),
942 "[method]resource1.do-something".to_string()
943 );
944 assert_eq!(
945 parsed,
946 ParsedFunctionName {
947 site: ParsedFunctionSite::PackagedInterface {
948 namespace: "ns".to_string(),
949 package: "name".to_string(),
950 interface: "interface".to_string(),
951 version: None,
952 },
953 function: ParsedFunctionReference::RawResourceMethod {
954 resource: "resource1".to_string(),
955 method: "do-something".to_string(),
956 },
957 }
958 );
959 }
960
961 #[test]
962 fn parse_function_name_method() {
963 let parsed =
964 ParsedFunctionName::parse("ns:name/interface.{[method]resource1.do-something}")
965 .expect("Parsing failed");
966 assert_eq!(
967 parsed.site().interface_name(),
968 Some("ns:name/interface".to_string())
969 );
970 assert_eq!(
971 parsed.function().function_name(),
972 "[method]resource1.do-something".to_string()
973 );
974 assert_eq!(
975 parsed,
976 ParsedFunctionName {
977 site: ParsedFunctionSite::PackagedInterface {
978 namespace: "ns".to_string(),
979 package: "name".to_string(),
980 interface: "interface".to_string(),
981 version: None,
982 },
983 function: ParsedFunctionReference::RawResourceMethod {
984 resource: "resource1".to_string(),
985 method: "do-something".to_string(),
986 },
987 }
988 );
989 }
990
991 #[test]
992 fn parse_function_name_static_method_syntax_sugar() {
993 let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.do-something-static}")
996 .expect("Parsing failed")
997 .method_as_static()
998 .unwrap();
999 assert_eq!(
1000 parsed.site().interface_name(),
1001 Some("ns:name/interface".to_string())
1002 );
1003 assert_eq!(
1004 parsed.function().function_name(),
1005 "[static]resource1.do-something-static".to_string()
1006 );
1007 assert_eq!(
1008 parsed,
1009 ParsedFunctionName {
1010 site: ParsedFunctionSite::PackagedInterface {
1011 namespace: "ns".to_string(),
1012 package: "name".to_string(),
1013 interface: "interface".to_string(),
1014 version: None,
1015 },
1016 function: ParsedFunctionReference::RawResourceStaticMethod {
1017 resource: "resource1".to_string(),
1018 method: "do-something-static".to_string(),
1019 },
1020 }
1021 );
1022 }
1023
1024 #[test]
1025 fn parse_function_name_static() {
1026 let parsed =
1027 ParsedFunctionName::parse("ns:name/interface.{[static]resource1.do-something-static}")
1028 .expect("Parsing failed");
1029 assert_eq!(
1030 parsed.site().interface_name(),
1031 Some("ns:name/interface".to_string())
1032 );
1033 assert_eq!(
1034 parsed.function().function_name(),
1035 "[static]resource1.do-something-static".to_string()
1036 );
1037 assert_eq!(
1038 parsed,
1039 ParsedFunctionName {
1040 site: ParsedFunctionSite::PackagedInterface {
1041 namespace: "ns".to_string(),
1042 package: "name".to_string(),
1043 interface: "interface".to_string(),
1044 version: None,
1045 },
1046 function: ParsedFunctionReference::RawResourceStaticMethod {
1047 resource: "resource1".to_string(),
1048 method: "do-something-static".to_string(),
1049 },
1050 }
1051 );
1052 }
1053
1054 #[test]
1055 fn parse_function_name_drop_syntax_sugar() {
1056 let parsed = ParsedFunctionName::parse("ns:name/interface.{resource1.drop}")
1057 .expect("Parsing failed");
1058 assert_eq!(
1059 parsed.site().interface_name(),
1060 Some("ns:name/interface".to_string())
1061 );
1062 assert_eq!(
1063 parsed.function().function_name(),
1064 "[drop]resource1".to_string()
1065 );
1066 assert_eq!(
1067 parsed,
1068 ParsedFunctionName {
1069 site: ParsedFunctionSite::PackagedInterface {
1070 namespace: "ns".to_string(),
1071 package: "name".to_string(),
1072 interface: "interface".to_string(),
1073 version: None,
1074 },
1075 function: ParsedFunctionReference::RawResourceDrop {
1076 resource: "resource1".to_string()
1077 },
1078 }
1079 );
1080 }
1081
1082 #[test]
1083 fn parse_function_name_drop() {
1084 let parsed = ParsedFunctionName::parse("ns:name/interface.{[drop]resource1}")
1085 .expect("Parsing failed");
1086 assert_eq!(
1087 parsed.site().interface_name(),
1088 Some("ns:name/interface".to_string())
1089 );
1090 assert_eq!(
1091 parsed.function().function_name(),
1092 "[drop]resource1".to_string()
1093 );
1094 assert_eq!(
1095 parsed,
1096 ParsedFunctionName {
1097 site: ParsedFunctionSite::PackagedInterface {
1098 namespace: "ns".to_string(),
1099 package: "name".to_string(),
1100 interface: "interface".to_string(),
1101 version: None,
1102 },
1103 function: ParsedFunctionReference::RawResourceDrop {
1104 resource: "resource1".to_string()
1105 },
1106 }
1107 );
1108 }
1109
1110 fn round_trip_function_name_parse(input: &str) {
1111 let parsed = ParsedFunctionName::parse(input)
1112 .unwrap_or_else(|_| panic!("Input Parsing failed for {input}"));
1113 let parsed_written =
1114 ParsedFunctionName::parse(parsed.to_string()).expect("Round-trip parsing failed");
1115 assert_eq!(parsed, parsed_written);
1116 }
1117
1118 #[test]
1119 fn test_parsed_function_name_display() {
1120 round_trip_function_name_parse("run-example");
1121 round_trip_function_name_parse("interface.{fn1}");
1122 round_trip_function_name_parse("wasi:cli/run@0.2.0.{run}");
1123 round_trip_function_name_parse("ns:name/interface.{resource1.new}");
1124 round_trip_function_name_parse("ns:name/interface.{[constructor]resource1}");
1125 round_trip_function_name_parse("ns:name/interface.{resource1.do-something}");
1126 round_trip_function_name_parse("ns:name/interface.{[static]resource1.do-something-static}");
1127 round_trip_function_name_parse("ns:name/interface.{resource1.drop}");
1128 round_trip_function_name_parse("ns:name/interface.{[drop]resource1}");
1129 }
1130}