@@ -10,6 +10,7 @@ pub mod role_index;
1010use std:: {
1111 fmt:: { Display , Formatter } ,
1212 str:: FromStr ,
13+ sync:: Arc ,
1314} ;
1415
1516use chrono:: { DateTime , Duration , Utc } ;
@@ -28,11 +29,21 @@ use role_index::RoleId;
2829/// Catalyst ID
2930/// <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/rbac_id_uri/catalyst-id-uri/>
3031///
31- /// Identity of Catalyst Registration.
32- /// Optionally also identifies a specific Signed Document Key
32+ /// Identity of Catalyst Registration. Optionally also identifies a specific Signed
33+ /// Document Key.
34+ ///
35+ /// `CatalystId` is an immutable data type: all modifying methods create a new instance.
36+ /// Also, this structure uses [`Arc`] internally, so it is cheap to clone.
3337#[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
3438#[ allow( clippy:: module_name_repetitions) ]
3539pub struct CatalystId {
40+ /// An inner data.
41+ inner : Arc < CatalystIdInner > ,
42+ }
43+
44+ /// A Catalyst ID data intended to be wrapper in `Arc`.
45+ #[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
46+ struct CatalystIdInner {
3647 /// Username
3748 username : Option < String > ,
3849 /// Nonce (like the password in http basic auth, but NOT a password, just a nonce)
@@ -73,49 +84,49 @@ impl CatalystId {
7384 /// Get the cosmetic username from the URI.
7485 #[ must_use]
7586 pub fn username ( & self ) -> Option < String > {
76- self . username . clone ( )
87+ self . inner . username . clone ( )
7788 }
7889
7990 /// Get the nonce from the URI.
8091 #[ must_use]
8192 pub fn nonce ( & self ) -> Option < DateTime < Utc > > {
82- self . nonce
93+ self . inner . nonce
8394 }
8495
8596 /// Get the network the `CatalystId` is referencing the registration to.
8697 #[ must_use]
8798 pub fn network ( & self ) -> ( String , Option < String > ) {
88- ( self . network . clone ( ) , self . subnet . clone ( ) )
99+ ( self . inner . network . clone ( ) , self . inner . subnet . clone ( ) )
89100 }
90101
91102 /// Is the key a signature type key.
92103 #[ must_use]
93104 pub fn is_signature_key ( & self ) -> bool {
94- !self . encryption
105+ !self . inner . encryption
95106 }
96107
97108 /// Is the key an encryption type key.
98109 #[ must_use]
99110 pub fn is_encryption_key ( & self ) -> bool {
100- self . encryption
111+ self . inner . encryption
101112 }
102113
103114 /// Get the Initial Role 0 Key of the registration
104115 #[ must_use]
105116 pub fn role0_pk ( & self ) -> VerifyingKey {
106- self . role0_pk
117+ self . inner . role0_pk
107118 }
108119
109120 /// Get the role index and its rotation count
110121 #[ must_use]
111122 pub fn role_and_rotation ( & self ) -> ( RoleId , KeyRotation ) {
112- ( self . role , self . rotation )
123+ ( self . inner . role , self . inner . rotation )
113124 }
114125
115126 /// Create a new `CatalystId` for a Signing Key
116127 #[ must_use]
117128 pub fn new ( network : & str , subnet : Option < & str > , role0_pk : VerifyingKey ) -> Self {
118- Self {
129+ let inner = Arc :: new ( CatalystIdInner {
119130 username : None , // Default to Not set, use `with_username` if required.
120131 nonce : None , // Default to Not set, use `with_nonce` if required.
121132 network : network. to_string ( ) ,
@@ -125,49 +136,59 @@ impl CatalystId {
125136 rotation : KeyRotation :: default ( ) , // Defaulted, use `with_rotation()` to change it.
126137 encryption : false , // Defaulted, use `with_encryption()` to change it.
127138 id : false , // Default to `URI` formatted.
128- }
139+ } ) ;
140+
141+ Self { inner }
129142 }
130143
131144 /// The `CatalystId` is formatted as a URI.
132145 #[ must_use]
133146 pub fn as_uri ( self ) -> Self {
134- Self { id : false , ..self }
147+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
148+ let inner = Arc :: new ( CatalystIdInner { id : false , ..inner } ) ;
149+ Self { inner }
135150 }
136151
137152 /// The `CatalystId` is formatted as a id.
138153 #[ must_use]
139154 pub fn as_id ( self ) -> Self {
140- Self { id : true , ..self }
155+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
156+ let inner = Arc :: new ( CatalystIdInner { id : true , ..inner } ) ;
157+ Self { inner }
141158 }
142159
143160 /// Was `CatalystId` formatted as an id when it was parsed.
144161 #[ must_use]
145162 pub fn is_id ( & self ) -> bool {
146- self . id
163+ self . inner . id
147164 }
148165
149166 /// Was `CatalystId` formatted as an uri when it was parsed.
150167 #[ must_use]
151168 pub fn is_uri ( & self ) -> bool {
152- !self . id
169+ !self . inner . id
153170 }
154171
155172 /// Add or change the username in a Catalyst ID URI.
156173 #[ must_use]
157174 pub fn with_username ( self , name : & str ) -> Self {
158- Self {
175+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
176+ let inner = Arc :: new ( CatalystIdInner {
159177 username : Some ( name. to_string ( ) ) ,
160- ..self
161- }
178+ ..inner
179+ } ) ;
180+ Self { inner }
162181 }
163182
164183 /// Add or change the username in a Catalyst ID URI.
165184 #[ must_use]
166185 pub fn without_username ( self ) -> Self {
167- Self {
186+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
187+ let inner = Arc :: new ( CatalystIdInner {
168188 username : None ,
169- ..self
170- }
189+ ..inner
190+ } ) ;
191+ Self { inner }
171192 }
172193
173194 /// Add or change the nonce (a unique identifier for a data update) to a specific
@@ -204,7 +225,9 @@ impl CatalystId {
204225 }
205226 } ;
206227
207- Self { nonce, ..self }
228+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
229+ let inner = Arc :: new ( CatalystIdInner { nonce, ..inner } ) ;
230+ Self { inner }
208231 }
209232
210233 /// Add or change the nonce in a Catalyst ID URI. The nonce will be set to the current
@@ -250,10 +273,12 @@ impl CatalystId {
250273 /// ```
251274 #[ must_use]
252275 pub fn without_nonce ( self ) -> Self {
253- Self {
276+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
277+ let inner = Arc :: new ( CatalystIdInner {
254278 nonce : None ,
255- ..self
256- }
279+ ..inner
280+ } ) ;
281+ Self { inner }
257282 }
258283
259284 /// Set that the `CatalystId` is used to identify an encryption key.
@@ -279,10 +304,12 @@ impl CatalystId {
279304 /// ```
280305 #[ must_use]
281306 pub fn with_encryption ( self ) -> Self {
282- Self {
307+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
308+ let inner = Arc :: new ( CatalystIdInner {
283309 encryption : true ,
284- ..self
285- }
310+ ..inner
311+ } ) ;
312+ Self { inner }
286313 }
287314
288315 /// Set that the `CatalystId` is not for encryption
@@ -308,10 +335,12 @@ impl CatalystId {
308335 /// ```
309336 #[ must_use]
310337 pub fn without_encryption ( self ) -> Self {
311- Self {
338+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
339+ let inner = Arc :: new ( CatalystIdInner {
312340 encryption : false ,
313- ..self
314- }
341+ ..inner
342+ } ) ;
343+ Self { inner }
315344 }
316345
317346 /// Set the role explicitly.
@@ -341,7 +370,9 @@ impl CatalystId {
341370 /// ```
342371 #[ must_use]
343372 pub fn with_role ( self , role : RoleId ) -> Self {
344- Self { role, ..self }
373+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
374+ let inner = Arc :: new ( CatalystIdInner { role, ..inner } ) ;
375+ Self { inner }
345376 }
346377
347378 /// Set the rotation explicitly.
@@ -370,7 +401,9 @@ impl CatalystId {
370401 /// ```
371402 #[ must_use]
372403 pub fn with_rotation ( self , rotation : KeyRotation ) -> Self {
373- Self { rotation, ..self }
404+ let inner = Arc :: try_unwrap ( self . inner ) . unwrap_or_else ( |v| ( * v) . clone ( ) ) ;
405+ let inner = Arc :: new ( CatalystIdInner { rotation, ..inner } ) ;
406+ Self { inner }
374407 }
375408
376409 /// Check if the URI has a nonce that falls within the defined boundary around `now()`
@@ -420,7 +453,7 @@ impl CatalystId {
420453 /// ```
421454 #[ must_use]
422455 pub fn is_nonce_in_range ( & self , past : Duration , future : Duration ) -> bool {
423- if let Some ( nonce) = self . nonce {
456+ if let Some ( nonce) = self . nonce ( ) {
424457 let now = Utc :: now ( ) ;
425458 let Some ( start_time) = now. checked_sub_signed ( past) else {
426459 return false ;
@@ -621,17 +654,17 @@ impl FromStr for CatalystId {
621654
622655impl Display for CatalystId {
623656 fn fmt ( & self , f : & mut Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
624- if !self . id {
657+ if !self . inner . id {
625658 write ! ( f, "{}://" , Self :: SCHEME . as_str( ) ) ?;
626659 }
627660
628661 let mut needs_at = false ;
629- if let Some ( username) = & self . username {
662+ if let Some ( username) = & self . inner . username {
630663 write ! ( f, "{username}" ) ?;
631664 needs_at = true ;
632665 }
633666
634- if let Some ( nonce) = self . nonce {
667+ if let Some ( nonce) = self . nonce ( ) {
635668 let timestamp = nonce. timestamp ( ) ;
636669 write ! ( f, ":{timestamp}" ) ?;
637670 needs_at = true ;
@@ -642,25 +675,25 @@ impl Display for CatalystId {
642675 write ! ( f, "@" ) ?;
643676 }
644677
645- if let Some ( subnet) = & self . subnet {
678+ if let Some ( subnet) = & self . inner . subnet {
646679 write ! ( f, "{subnet}." ) ?;
647680 }
648681 write ! (
649682 f,
650683 "{}/{}" ,
651- self . network,
652- base64_url:: encode( self . role0_pk. as_bytes( ) ) ,
684+ self . inner . network,
685+ base64_url:: encode( self . role0_pk( ) . as_bytes( ) ) ,
653686 ) ?;
654687
655688 // Role and Rotation are only serialized if its NOT and ID or they are not the defaults.
656- if !self . role . is_default ( ) || !self . rotation . is_default ( ) || !self . id {
657- write ! ( f, "/{}" , self . role) ?;
658- if !self . rotation . is_default ( ) || !self . id {
659- write ! ( f, "/{}" , self . rotation) ?;
689+ if !self . inner . role . is_default ( ) || !self . inner . rotation . is_default ( ) || !self . inner . id {
690+ write ! ( f, "/{}" , self . inner . role) ?;
691+ if !self . inner . rotation . is_default ( ) || !self . inner . id {
692+ write ! ( f, "/{}" , self . inner . rotation) ?;
660693 }
661694 }
662695
663- if self . encryption {
696+ if self . inner . encryption {
664697 write ! ( f, "#{}" , Self :: ENCRYPTION_FRAGMENT ) ?;
665698 }
666699 Ok ( ( ) )
0 commit comments