@@ -63,14 +63,24 @@ struct CatalystIdInner {
6363 /// - `true`: The key is used for encryption.
6464 /// - `false`: The key is used for signing (signature key).
6565 encryption : bool ,
66- /// Indicates if this is an `id` type, or a `uri` type.
67- /// Used by the serialization functions.
68- /// `true` = format as an `Id`
69- /// `false` = format as a `Uri`
70- id : bool ,
66+ /// Catalyst ID type (URI, ID or Admin URI)
67+ r#type : CatalystIdType ,
68+ }
69+
70+ #[ derive( Debug , Clone , Default ) ]
71+ enum CatalystIdType {
72+ /// format as an `Id`
73+ Id ,
74+ /// format as a `Uri`
75+ #[ default]
76+ Uri ,
77+ /// format as a admin `Uri`
78+ AdminUri ,
7179}
7280
7381impl CatalystId {
82+ /// Admin URI scheme for Catalyst
83+ pub const ADMIN_SCHEME : & Scheme = Scheme :: new_or_panic ( "admin.catalyst" ) ;
7484 /// Encryption Key Identifier Fragment
7585 const ENCRYPTION_FRAGMENT : & EStr < Fragment > = EStr :: new_or_panic ( "encrypt" ) ;
7686 /// Maximum allowable Nonce Value
@@ -140,7 +150,7 @@ impl CatalystId {
140150 role : RoleId :: default ( ) , // Defaulted, use `with_role()` to change it.
141151 rotation : KeyRotation :: default ( ) , // Defaulted, use `with_rotation()` to change it.
142152 encryption : false , // Defaulted, use `with_encryption()` to change it.
143- id : false , // Default to `URI` formatted.
153+ r#type : CatalystIdType :: default ( ) , // Default to `URI` formatted.
144154 } ) ;
145155
146156 Self { inner }
@@ -150,28 +160,51 @@ impl CatalystId {
150160 #[ must_use]
151161 pub fn as_uri ( self ) -> Self {
152162 let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
153- let inner = Arc :: new ( CatalystIdInner { id : false , ..inner } ) ;
163+ let inner = Arc :: new ( CatalystIdInner {
164+ r#type : CatalystIdType :: Uri ,
165+ ..inner
166+ } ) ;
154167 Self { inner }
155168 }
156169
157170 /// The `CatalystId` is formatted as a id.
158171 #[ must_use]
159172 pub fn as_id ( self ) -> Self {
160173 let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
161- let inner = Arc :: new ( CatalystIdInner { id : true , ..inner } ) ;
174+ let inner = Arc :: new ( CatalystIdInner {
175+ r#type : CatalystIdType :: Id ,
176+ ..inner
177+ } ) ;
178+ Self { inner }
179+ }
180+
181+ /// The `CatalystId` is formatted as a admin URI.
182+ #[ must_use]
183+ pub fn as_admin ( self ) -> Self {
184+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
185+ let inner = Arc :: new ( CatalystIdInner {
186+ r#type : CatalystIdType :: AdminUri ,
187+ ..inner
188+ } ) ;
162189 Self { inner }
163190 }
164191
165192 /// Was `CatalystId` formatted as an id when it was parsed.
166193 #[ must_use]
167194 pub fn is_id ( & self ) -> bool {
168- self . inner . id
195+ matches ! ( self . inner. r#type, CatalystIdType :: Id )
196+ }
197+
198+ /// Is `CatalystId` formatted as an Admin.
199+ #[ must_use]
200+ pub fn is_admin ( & self ) -> bool {
201+ matches ! ( self . inner. r#type, CatalystIdType :: AdminUri )
169202 }
170203
171204 /// Was `CatalystId` formatted as an uri when it was parsed.
172205 #[ must_use]
173206 pub fn is_uri ( & self ) -> bool {
174- ! self . inner . id
207+ matches ! ( self . inner. r#type , CatalystIdType :: Uri )
175208 }
176209
177210 /// Add or change the username in a Catalyst ID URI.
@@ -588,27 +621,26 @@ impl FromStr for CatalystId {
588621 /// This will parse a URI or a RAW ID.\
589622 /// The only difference between them is a URI has the scheme, a raw ID does not.
590623 fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
591- // Did we serialize an ID?
592- let mut id = false ;
593-
594- // Check if we have a scheme, and if not default it to the catalyst ID scheme.
595- let raw_uri = {
624+ let ( uri, r#type) = {
596625 if s. contains ( "://" ) {
597- s. to_owned ( )
626+ let uri = Uri :: parse ( s. to_owned ( ) ) ?;
627+ // Check if its the correct scheme.
628+ let r#type = if uri. scheme ( ) == Self :: SCHEME {
629+ CatalystIdType :: Uri
630+ } else if uri. scheme ( ) == Self :: ADMIN_SCHEME {
631+ CatalystIdType :: AdminUri
632+ } else {
633+ return Err ( errors:: CatalystIdError :: InvalidScheme ) ;
634+ } ;
635+ ( uri, r#type)
598636 } else {
599- id = true ;
600637 // It might be a RAW ID, so try and parse with the correct scheme.
601- format ! ( "{}://{}" , CatalystId :: SCHEME , s)
638+ let uri = Uri :: parse ( format ! ( "{}://{}" , Self :: SCHEME , s) ) ?;
639+ let r#type = CatalystIdType :: Id ;
640+ ( uri, r#type)
602641 }
603642 } ;
604643
605- let uri = Uri :: parse ( raw_uri) ?;
606-
607- // Check if its the correct scheme.
608- if uri. scheme ( ) != CatalystId :: SCHEME {
609- return Err ( errors:: CatalystIdError :: InvalidScheme ) ;
610- }
611-
612644 // Decode the network and subnet
613645 let auth = uri
614646 . authority ( )
@@ -684,36 +716,26 @@ impl FromStr for CatalystId {
684716 }
685717 } ;
686718
687- let cat_id = {
688- let mut cat_id = Self :: new ( network, subnet, role0_pk)
689- . with_role ( role_index)
690- . with_rotation ( rotation) ;
691-
692- if uri. has_fragment ( ) {
693- if uri. fragment ( ) == Some ( Self :: ENCRYPTION_FRAGMENT ) {
694- cat_id = cat_id. with_encryption ( ) ;
695- } else {
696- return Err ( errors:: CatalystIdError :: InvalidEncryptionKeyFragment ) ;
697- }
698- }
699-
700- if let Some ( username) = username {
701- cat_id = cat_id. with_username ( & username) ;
702- }
703-
704- if let Some ( nonce) = nonce {
705- cat_id = cat_id. with_specific_nonce ( nonce) ;
706- }
707-
708- // Default to URI, so only set it as an ID if its not a URI.
709- if id {
710- cat_id = cat_id. as_id ( ) ;
711- }
712-
713- cat_id
719+ let encryption = match uri. fragment ( ) {
720+ None => false ,
721+ Some ( f) if f == Self :: ENCRYPTION_FRAGMENT => true ,
722+ Some ( _) => return Err ( errors:: CatalystIdError :: InvalidEncryptionKeyFragment ) ,
714723 } ;
715724
716- Ok ( cat_id)
725+ let inner = CatalystIdInner {
726+ network : network. to_string ( ) ,
727+ subnet : subnet. map ( ToString :: to_string) ,
728+ role0_pk,
729+ r#type,
730+ rotation,
731+ role : role_index,
732+ username,
733+ nonce,
734+ encryption,
735+ }
736+ . into ( ) ;
737+
738+ Ok ( Self { inner } )
717739 }
718740}
719741
@@ -722,8 +744,10 @@ impl Display for CatalystId {
722744 & self ,
723745 f : & mut Formatter < ' _ > ,
724746 ) -> Result < ( ) , std:: fmt:: Error > {
725- if !self . inner . id {
726- write ! ( f, "{}://" , Self :: SCHEME . as_str( ) ) ?;
747+ match self . inner . r#type {
748+ CatalystIdType :: Uri => write ! ( f, "{}://" , Self :: SCHEME . as_str( ) ) ?,
749+ CatalystIdType :: AdminUri => write ! ( f, "{}://" , Self :: ADMIN_SCHEME . as_str( ) ) ?,
750+ CatalystIdType :: Id => { } ,
727751 }
728752
729753 let mut needs_at = false ;
@@ -754,9 +778,9 @@ impl Display for CatalystId {
754778 ) ?;
755779
756780 // Role and Rotation are only serialized if its NOT and ID or they are not the defaults.
757- if !self . inner . role . is_default ( ) || !self . inner . rotation . is_default ( ) || !self . inner . id {
781+ if !self . inner . role . is_default ( ) || !self . inner . rotation . is_default ( ) || !self . is_id ( ) {
758782 write ! ( f, "/{}" , self . inner. role) ?;
759- if !self . inner . rotation . is_default ( ) || !self . inner . id {
783+ if !self . inner . rotation . is_default ( ) || !self . is_id ( ) {
760784 write ! ( f, "/{}" , self . inner. rotation) ?;
761785 }
762786 }
@@ -792,7 +816,7 @@ mod tests {
792816
793817 use super :: CatalystId ;
794818
795- const CATALYST_ID_TEST_VECTOR : [ & str ; 9 ] = [
819+ const CATALYST_ID_TEST_VECTOR : [ & str ; 13 ] = [
796820 "cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE" ,
797821 "user@cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE" ,
798822 "user:1735689600@cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE" ,
@@ -802,6 +826,11 @@ mod tests {
802826 "id.catalyst://preview.cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/2/0#encrypt" ,
803827 "id.catalyst://midnight/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/0/1" ,
804828 "id.catalyst://midnight/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/2/1#encrypt" ,
829+ // Admin types
830+ "admin.catalyst://preprod.cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/7/3" ,
831+ "admin.catalyst://preview.cardano/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/2/0#encrypt" ,
832+ "admin.catalyst://midnight/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/0/1" ,
833+ "admin.catalyst://midnight/FftxFnOrj2qmTuB2oZG2v0YEWJfKvQ9Gg8AgNAhDsKE/2/1#encrypt" ,
805834 ] ;
806835
807836 #[ test]
@@ -845,6 +874,22 @@ mod tests {
845874 assert_eq ! ( uri_id. as_short_id( ) . inner, short_id. inner) ;
846875 }
847876
877+ #[ test]
878+ fn catalyst_id_type_test ( ) {
879+ for id_string in & CATALYST_ID_TEST_VECTOR [ 0 ..5 ] {
880+ let id = id_string. parse :: < CatalystId > ( ) . unwrap ( ) ;
881+ assert ! ( id. is_id( ) ) ;
882+ }
883+ for id_string in & CATALYST_ID_TEST_VECTOR [ 5 ..9 ] {
884+ let id = id_string. parse :: < CatalystId > ( ) . unwrap ( ) ;
885+ assert ! ( id. is_uri( ) ) ;
886+ }
887+ for id_string in & CATALYST_ID_TEST_VECTOR [ 9 ..13 ] {
888+ let id = id_string. parse :: < CatalystId > ( ) . unwrap ( ) ;
889+ assert ! ( id. is_admin( ) ) ;
890+ }
891+ }
892+
848893 #[ test_case( 0 , 1 , true , false , false ; "base vs user" ) ]
849894 #[ test_case( 0 , 2 , true , false , false ; "base vs user_nonce" ) ]
850895 #[ test_case( 0 , 3 , true , false , false ; "base vs nonce" ) ]
0 commit comments