@@ -242,6 +242,7 @@ impl From<KebabString> for String {
242242/// * a plain method name : `[method]a-b.c-d`
243243/// * a plain static method name : `[static]a-b.c-d`
244244/// * a plain constructor: `[constructor]a-b`
245+ /// * an implements name: `[implements=<a:b/c>]label`
245246/// * an interface name: `wasi:cli/reactor@0.1.0`
246247/// * a dependency name: `locked-dep=foo:bar/baz`
247248/// * a URL name: `url=https://..`
@@ -252,6 +253,10 @@ impl From<KebabString> for String {
252253/// Note that this type the `[method]...` and `[static]...` variants are
253254/// considered equal and hash to the same value. This enables disallowing
254255/// clashes between the two where method name overlap cannot happen.
256+ ///
257+ /// Similarly, `[implements=<I>]L` is considered equal to a bare label `L`
258+ /// (they conflict on the same label). Two `[implements=<...>]` names with
259+ /// the same label also conflict, regardless of the interface name.
255260#[ derive( Clone ) ]
256261pub struct ComponentName {
257262 raw : String ,
@@ -264,6 +269,7 @@ enum ParsedComponentNameKind {
264269 Constructor ,
265270 Method ,
266271 Static ,
272+ Implements ,
267273 Interface ,
268274 Dependency ,
269275 Url ,
@@ -283,6 +289,9 @@ pub enum ComponentNameKind<'a> {
283289 /// `[static]a-b.c-d`
284290 #[ allow( missing_docs) ]
285291 Static ( ResourceFunc < ' a > ) ,
292+ /// A plain-named instance that implements a named interface, e.g.
293+ /// `[implements=<a:b/c>]label`.
294+ Implements ( ImplementsName < ' a > ) ,
286295 /// `wasi:http/types@2.0`
287296 #[ allow( missing_docs) ]
288297 Interface ( InterfaceName < ' a > ) ,
@@ -300,6 +309,7 @@ pub enum ComponentNameKind<'a> {
300309const CONSTRUCTOR : & str = "[constructor]" ;
301310const METHOD : & str = "[method]" ;
302311const STATIC : & str = "[static]" ;
312+ const IMPLEMENTS_PREFIX : & str = "[implements=<" ;
303313
304314impl ComponentName {
305315 /// Attempts to parse `name` as a valid component name, returning `Err` if
@@ -338,6 +348,7 @@ impl ComponentName {
338348 PK :: Constructor => Constructor ( KebabStr :: new_unchecked ( & self . raw [ CONSTRUCTOR . len ( ) ..] ) ) ,
339349 PK :: Method => Method ( ResourceFunc ( & self . raw [ METHOD . len ( ) ..] ) ) ,
340350 PK :: Static => Static ( ResourceFunc ( & self . raw [ STATIC . len ( ) ..] ) ) ,
351+ PK :: Implements => Implements ( ImplementsName ( & self . raw [ IMPLEMENTS_PREFIX . len ( ) ..] ) ) ,
341352 PK :: Interface => Interface ( InterfaceName ( & self . raw ) ) ,
342353 PK :: Dependency => Dependency ( DependencyName ( & self . raw ) ) ,
343354 PK :: Url => Url ( UrlName ( & self . raw ) ) ,
@@ -379,7 +390,7 @@ impl Ord for ComponentName {
379390
380391impl PartialOrd for ComponentName {
381392 fn partial_cmp ( & self , other : & Self ) -> Option < Ordering > {
382- self . kind . partial_cmp ( & other. kind )
393+ Some ( self . kind ( ) . cmp ( & other. kind ( ) ) )
383394 }
384395}
385396
@@ -397,9 +408,13 @@ impl fmt::Debug for ComponentName {
397408
398409impl ComponentNameKind < ' _ > {
399410 /// Returns the [`ParsedComponentNameKind`] of the [`ComponentNameKind`].
411+ ///
412+ /// Note that `Implements` maps to `Label` because they share the same
413+ /// uniqueness namespace (an `[implements=<I>]L` name conflicts with a
414+ /// bare label `L`).
400415 fn kind ( & self ) -> ParsedComponentNameKind {
401416 match self {
402- Self :: Label ( _) => ParsedComponentNameKind :: Label ,
417+ Self :: Label ( _) | Self :: Implements ( _ ) => ParsedComponentNameKind :: Label ,
403418 Self :: Constructor ( _) => ParsedComponentNameKind :: Constructor ,
404419 Self :: Method ( _) => ParsedComponentNameKind :: Method ,
405420 Self :: Static ( _) => ParsedComponentNameKind :: Static ,
@@ -428,12 +443,29 @@ impl Ord for ComponentNameKind<'_> {
428443 Ordering :: Equal
429444 }
430445
446+ // `[implements=<I>]L` compares by label only, and is equivalent
447+ // to a bare label `L`.
448+ ( Implements ( lhs) , Implements ( rhs) ) => lhs. label ( ) . cmp ( rhs. label ( ) ) ,
449+ ( Label ( lhs) , Implements ( rhs) ) => ( * lhs) . cmp ( rhs. label ( ) ) ,
450+ ( Implements ( lhs) , Label ( rhs) ) => lhs. label ( ) . cmp ( * rhs) ,
451+
452+ // `[implements=<I>]l` is equivalent to `[method]l.l` / `[static]l.l`
453+ // when resource == method (the `l.l` edge case that also equals
454+ // bare label `l`).
455+ ( Implements ( imp) , Method ( method) | Static ( method) )
456+ | ( Method ( method) | Static ( method) , Implements ( imp) )
457+ if imp. label ( ) == method. resource ( ) && imp. label ( ) == method. method ( ) =>
458+ {
459+ Ordering :: Equal
460+ }
461+
431462 ( Interface ( lhs) , Interface ( rhs) ) => lhs. cmp ( rhs) ,
432463 ( Dependency ( lhs) , Dependency ( rhs) ) => lhs. cmp ( rhs) ,
433464 ( Url ( lhs) , Url ( rhs) ) => lhs. cmp ( rhs) ,
434465 ( Hash ( lhs) , Hash ( rhs) ) => lhs. cmp ( rhs) ,
435466
436467 ( Label ( _) , _)
468+ | ( Implements ( _) , _)
437469 | ( Constructor ( _) , _)
438470 | ( Method ( _) , _)
439471 | ( Static ( _) , _)
@@ -455,7 +487,11 @@ impl Hash for ComponentNameKind<'_> {
455487 fn hash < H : Hasher > ( & self , hasher : & mut H ) {
456488 use ComponentNameKind :: * ;
457489 match self {
490+ // `[implements=<I>]L` hashes the same as bare label `L` since
491+ // they conflict on the same label.
458492 Label ( name) => ( 0u8 , name) . hash ( hasher) ,
493+ Implements ( name) => ( 0u8 , name. label ( ) ) . hash ( hasher) ,
494+
459495 Constructor ( name) => ( 1u8 , name) . hash ( hasher) ,
460496
461497 Method ( name) | Static ( name) => {
@@ -508,6 +544,37 @@ impl<'a> ResourceFunc<'a> {
508544 }
509545}
510546
547+ /// An implements name, representing `[implements=<I>]L`.
548+ ///
549+ /// The internal string starts after `[implements=<` and contains
550+ /// `interface_name>]label`.
551+ #[ derive( Debug , Clone ) ]
552+ pub struct ImplementsName < ' a > ( & ' a str ) ;
553+
554+ impl < ' a > ImplementsName < ' a > {
555+ /// Returns the full raw string of the implements name (everything after
556+ /// the `[implements=<` prefix).
557+ pub fn as_str ( & self ) -> & ' a str {
558+ self . 0
559+ }
560+
561+ /// Returns the index of `>]` in the internal string, which separates the
562+ /// interface name from the label.
563+ fn split_point ( & self ) -> usize {
564+ self . 0 . find ( ">]" ) . unwrap ( )
565+ }
566+
567+ /// Returns the interface name (the `I` in `[implements=<I>]L`).
568+ pub fn interface ( & self ) -> & ' a str {
569+ & self . 0 [ ..self . split_point ( ) ]
570+ }
571+
572+ /// Returns the label (the `L` in `[implements=<I>]L`).
573+ pub fn label ( & self ) -> & ' a KebabStr {
574+ KebabStr :: new_unchecked ( & self . 0 [ self . split_point ( ) + 2 ..] )
575+ }
576+ }
577+
511578/// An interface name, stored as `a:b/c@1.2.3`
512579#[ derive( Debug , Clone , Hash , Eq , PartialEq , Ord , PartialOrd ) ]
513580pub struct InterfaceName < ' a > ( & ' a str ) ;
@@ -616,6 +683,29 @@ impl<'a> ComponentNameParser<'a> {
616683 return Ok ( ParsedComponentNameKind :: Static ) ;
617684 }
618685
686+ // '[implements=<' <interfacename> '>]' <label>
687+ if self . eat_str ( IMPLEMENTS_PREFIX ) {
688+ let iface_str = self . take_up_to ( '>' ) ?;
689+ // Validate the interface name by parsing it as a package name
690+ // with a required projection (e.g. `ns:pkg/iface`).
691+ let mut iface_parser = ComponentNameParser {
692+ next : iface_str,
693+ offset : self . offset ,
694+ features : self . features ,
695+ } ;
696+ iface_parser. pkg_name ( true ) ?;
697+ if !iface_parser. next . is_empty ( ) {
698+ bail ! (
699+ self . offset,
700+ "trailing content after interface name in implements annotation"
701+ ) ;
702+ }
703+ self . expect_str ( ">" ) ?;
704+ self . expect_str ( "]" ) ?;
705+ self . expect_kebab ( ) ?;
706+ return Ok ( ParsedComponentNameKind :: Implements ) ;
707+ }
708+
619709 // 'unlocked-dep=<' <pkgnamequery> '>'
620710 if self . eat_str ( "unlocked-dep=" ) {
621711 self . expect_str ( "<" ) ?;
@@ -962,6 +1052,19 @@ mod tests {
9621052 assert ! ( parse_kebab_name( "[method]a.b.c" ) . is_none( ) ) ;
9631053 assert ! ( parse_kebab_name( "[static]a.b" ) . is_some( ) ) ;
9641054 assert ! ( parse_kebab_name( "[static]a" ) . is_none( ) ) ;
1055+
1056+ // implements names
1057+ assert ! ( parse_kebab_name( "[implements=<a:b/c>]name" ) . is_some( ) ) ;
1058+ assert ! ( parse_kebab_name( "[implements=<a:b/c@1.0.0>]name" ) . is_some( ) ) ;
1059+ assert ! ( parse_kebab_name( "[implements=<ns:pkg/iface>]my-label" ) . is_some( ) ) ;
1060+ // invalid: not a valid interface name (no colon/slash)
1061+ assert ! ( parse_kebab_name( "[implements=<not-valid>]name" ) . is_none( ) ) ;
1062+ // invalid: empty interface name
1063+ assert ! ( parse_kebab_name( "[implements=<>]name" ) . is_none( ) ) ;
1064+ // invalid: missing label
1065+ assert ! ( parse_kebab_name( "[implements=<a:b/c>]" ) . is_none( ) ) ;
1066+ // invalid: label not kebab
1067+ assert ! ( parse_kebab_name( "[implements=<a:b/c>]NOT_KEBAB" ) . is_none( ) ) ;
9651068 }
9661069
9671070 #[ test]
@@ -1014,4 +1117,105 @@ mod tests {
10141117 assert ! ( !s. insert( parse_kebab_name( "[static]a.b" ) ) ) ;
10151118 assert ! ( s. insert( parse_kebab_name( "[static]b.b" ) ) ) ;
10161119 }
1120+
1121+ #[ test]
1122+ fn implements_name_parts ( ) {
1123+ let name = parse_kebab_name ( "[implements=<a:b/c>]my-label" ) . unwrap ( ) ;
1124+ match name. kind ( ) {
1125+ ComponentNameKind :: Implements ( imp) => {
1126+ assert_eq ! ( imp. interface( ) , "a:b/c" ) ;
1127+ assert_eq ! ( imp. label( ) . as_str( ) , "my-label" ) ;
1128+ }
1129+ other => panic ! ( "expected Implements, got {other:?}" ) ,
1130+ }
1131+
1132+ let name = parse_kebab_name ( "[implements=<ns:pkg/iface@1.2.3>]the-name" ) . unwrap ( ) ;
1133+ match name. kind ( ) {
1134+ ComponentNameKind :: Implements ( imp) => {
1135+ assert_eq ! ( imp. interface( ) , "ns:pkg/iface@1.2.3" ) ;
1136+ assert_eq ! ( imp. label( ) . as_str( ) , "the-name" ) ;
1137+ }
1138+ other => panic ! ( "expected Implements, got {other:?}" ) ,
1139+ }
1140+ }
1141+
1142+ #[ test]
1143+ fn implements_name_equality ( ) {
1144+ // Same label conflicts, even with different interfaces
1145+ assert_eq ! (
1146+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1147+ parse_kebab_name( "[implements=<x:y/z>]name" ) ,
1148+ ) ;
1149+
1150+ // Implements conflicts with bare label
1151+ assert_eq ! (
1152+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1153+ parse_kebab_name( "name" ) ,
1154+ ) ;
1155+
1156+ // Different labels are strongly unique
1157+ assert_ne ! (
1158+ parse_kebab_name( "[implements=<a:b/c>]one" ) ,
1159+ parse_kebab_name( "[implements=<a:b/c>]two" ) ,
1160+ ) ;
1161+
1162+ // Implements is strongly unique from interface names
1163+ assert_ne ! (
1164+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1165+ parse_kebab_name( "a:b/c" ) ,
1166+ ) ;
1167+
1168+ // HashSet uniqueness
1169+ let mut s = HashSet :: new ( ) ;
1170+ assert ! ( s. insert( parse_kebab_name( "[implements=<a:b/c>]one" ) ) ) ;
1171+ assert ! ( s. insert( parse_kebab_name( "[implements=<a:b/c>]two" ) ) ) ;
1172+ // same label conflicts
1173+ assert ! ( !s. insert( parse_kebab_name( "[implements=<x:y/z>]one" ) ) ) ;
1174+ // bare label conflicts with implements label
1175+ assert ! ( !s. insert( parse_kebab_name( "one" ) ) ) ;
1176+ // interface name is a different kind, no conflict
1177+ assert ! ( s. insert( parse_kebab_name( "a:b/c" ) ) ) ;
1178+ }
1179+
1180+ #[ test]
1181+ fn implements_cross_kind_uniqueness ( ) {
1182+ // Implements is strongly unique from constructor
1183+ assert_ne ! (
1184+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1185+ parse_kebab_name( "[constructor]name" ) ,
1186+ ) ;
1187+
1188+ // Implements is strongly unique from method (different resource.method)
1189+ assert_ne ! (
1190+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1191+ parse_kebab_name( "[method]name.other" ) ,
1192+ ) ;
1193+
1194+ // Implements is strongly unique from static (different resource.method)
1195+ assert_ne ! (
1196+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1197+ parse_kebab_name( "[static]name.other" ) ,
1198+ ) ;
1199+
1200+ // The l.l edge case: [method]name.name equals bare "name",
1201+ // and [implements=<I>]name also equals bare "name",
1202+ // so they must be equal to each other.
1203+ assert_eq ! (
1204+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1205+ parse_kebab_name( "[method]name.name" ) ,
1206+ ) ;
1207+ assert_eq ! (
1208+ parse_kebab_name( "[implements=<a:b/c>]name" ) ,
1209+ parse_kebab_name( "[static]name.name" ) ,
1210+ ) ;
1211+
1212+ // HashSet: implements, constructor, and method with different
1213+ // resource.method should all coexist
1214+ let mut s = HashSet :: new ( ) ;
1215+ assert ! ( s. insert( parse_kebab_name( "[implements=<a:b/c>]name" ) ) ) ;
1216+ assert ! ( s. insert( parse_kebab_name( "[constructor]name" ) ) ) ;
1217+ assert ! ( s. insert( parse_kebab_name( "[method]name.other" ) ) ) ;
1218+ // But [method]name.name conflicts (l.l edge case)
1219+ assert ! ( !s. insert( parse_kebab_name( "[method]name.name" ) ) ) ;
1220+ }
10171221}
0 commit comments