@@ -10,6 +10,7 @@ pub mod role_index;
10
10
use std:: {
11
11
fmt:: { Display , Formatter } ,
12
12
str:: FromStr ,
13
+ sync:: Arc ,
13
14
} ;
14
15
15
16
use chrono:: { DateTime , Duration , Utc } ;
@@ -28,11 +29,21 @@ use role_index::RoleId;
28
29
/// Catalyst ID
29
30
/// <https://input-output-hk.github.io/catalyst-libs/architecture/08_concepts/rbac_id_uri/catalyst-id-uri/>
30
31
///
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.
33
37
#[ derive( Debug , Clone , PartialEq , Eq , Hash ) ]
34
38
#[ allow( clippy:: module_name_repetitions) ]
35
39
pub 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 {
36
47
/// Username
37
48
username : Option < String > ,
38
49
/// Nonce (like the password in http basic auth, but NOT a password, just a nonce)
@@ -73,49 +84,49 @@ impl CatalystId {
73
84
/// Get the cosmetic username from the URI.
74
85
#[ must_use]
75
86
pub fn username ( & self ) -> Option < String > {
76
- self . username . clone ( )
87
+ self . inner . username . clone ( )
77
88
}
78
89
79
90
/// Get the nonce from the URI.
80
91
#[ must_use]
81
92
pub fn nonce ( & self ) -> Option < DateTime < Utc > > {
82
- self . nonce
93
+ self . inner . nonce
83
94
}
84
95
85
96
/// Get the network the `CatalystId` is referencing the registration to.
86
97
#[ must_use]
87
98
pub fn network ( & self ) -> ( String , Option < String > ) {
88
- ( self . network . clone ( ) , self . subnet . clone ( ) )
99
+ ( self . inner . network . clone ( ) , self . inner . subnet . clone ( ) )
89
100
}
90
101
91
102
/// Is the key a signature type key.
92
103
#[ must_use]
93
104
pub fn is_signature_key ( & self ) -> bool {
94
- !self . encryption
105
+ !self . inner . encryption
95
106
}
96
107
97
108
/// Is the key an encryption type key.
98
109
#[ must_use]
99
110
pub fn is_encryption_key ( & self ) -> bool {
100
- self . encryption
111
+ self . inner . encryption
101
112
}
102
113
103
114
/// Get the Initial Role 0 Key of the registration
104
115
#[ must_use]
105
116
pub fn role0_pk ( & self ) -> VerifyingKey {
106
- self . role0_pk
117
+ self . inner . role0_pk
107
118
}
108
119
109
120
/// Get the role index and its rotation count
110
121
#[ must_use]
111
122
pub fn role_and_rotation ( & self ) -> ( RoleId , KeyRotation ) {
112
- ( self . role , self . rotation )
123
+ ( self . inner . role , self . inner . rotation )
113
124
}
114
125
115
126
/// Create a new `CatalystId` for a Signing Key
116
127
#[ must_use]
117
128
pub fn new ( network : & str , subnet : Option < & str > , role0_pk : VerifyingKey ) -> Self {
118
- Self {
129
+ let inner = Arc :: new ( CatalystIdInner {
119
130
username : None , // Default to Not set, use `with_username` if required.
120
131
nonce : None , // Default to Not set, use `with_nonce` if required.
121
132
network : network. to_string ( ) ,
@@ -125,49 +136,59 @@ impl CatalystId {
125
136
rotation : KeyRotation :: default ( ) , // Defaulted, use `with_rotation()` to change it.
126
137
encryption : false , // Defaulted, use `with_encryption()` to change it.
127
138
id : false , // Default to `URI` formatted.
128
- }
139
+ } ) ;
140
+
141
+ Self { inner }
129
142
}
130
143
131
144
/// The `CatalystId` is formatted as a URI.
132
145
#[ must_use]
133
146
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 }
135
150
}
136
151
137
152
/// The `CatalystId` is formatted as a id.
138
153
#[ must_use]
139
154
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 }
141
158
}
142
159
143
160
/// Was `CatalystId` formatted as an id when it was parsed.
144
161
#[ must_use]
145
162
pub fn is_id ( & self ) -> bool {
146
- self . id
163
+ self . inner . id
147
164
}
148
165
149
166
/// Was `CatalystId` formatted as an uri when it was parsed.
150
167
#[ must_use]
151
168
pub fn is_uri ( & self ) -> bool {
152
- !self . id
169
+ !self . inner . id
153
170
}
154
171
155
172
/// Add or change the username in a Catalyst ID URI.
156
173
#[ must_use]
157
174
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 {
159
177
username : Some ( name. to_string ( ) ) ,
160
- ..self
161
- }
178
+ ..inner
179
+ } ) ;
180
+ Self { inner }
162
181
}
163
182
164
183
/// Add or change the username in a Catalyst ID URI.
165
184
#[ must_use]
166
185
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 {
168
188
username : None ,
169
- ..self
170
- }
189
+ ..inner
190
+ } ) ;
191
+ Self { inner }
171
192
}
172
193
173
194
/// Add or change the nonce (a unique identifier for a data update) to a specific
@@ -204,7 +225,9 @@ impl CatalystId {
204
225
}
205
226
} ;
206
227
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 }
208
231
}
209
232
210
233
/// Add or change the nonce in a Catalyst ID URI. The nonce will be set to the current
@@ -250,10 +273,12 @@ impl CatalystId {
250
273
/// ```
251
274
#[ must_use]
252
275
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 {
254
278
nonce : None ,
255
- ..self
256
- }
279
+ ..inner
280
+ } ) ;
281
+ Self { inner }
257
282
}
258
283
259
284
/// Set that the `CatalystId` is used to identify an encryption key.
@@ -279,10 +304,12 @@ impl CatalystId {
279
304
/// ```
280
305
#[ must_use]
281
306
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 {
283
309
encryption : true ,
284
- ..self
285
- }
310
+ ..inner
311
+ } ) ;
312
+ Self { inner }
286
313
}
287
314
288
315
/// Set that the `CatalystId` is not for encryption
@@ -308,10 +335,12 @@ impl CatalystId {
308
335
/// ```
309
336
#[ must_use]
310
337
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 {
312
340
encryption : false ,
313
- ..self
314
- }
341
+ ..inner
342
+ } ) ;
343
+ Self { inner }
315
344
}
316
345
317
346
/// Set the role explicitly.
@@ -341,7 +370,9 @@ impl CatalystId {
341
370
/// ```
342
371
#[ must_use]
343
372
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 }
345
376
}
346
377
347
378
/// Set the rotation explicitly.
@@ -370,7 +401,9 @@ impl CatalystId {
370
401
/// ```
371
402
#[ must_use]
372
403
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 }
374
407
}
375
408
376
409
/// Check if the URI has a nonce that falls within the defined boundary around `now()`
@@ -420,7 +453,7 @@ impl CatalystId {
420
453
/// ```
421
454
#[ must_use]
422
455
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 ( ) {
424
457
let now = Utc :: now ( ) ;
425
458
let Some ( start_time) = now. checked_sub_signed ( past) else {
426
459
return false ;
@@ -621,17 +654,17 @@ impl FromStr for CatalystId {
621
654
622
655
impl Display for CatalystId {
623
656
fn fmt ( & self , f : & mut Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
624
- if !self . id {
657
+ if !self . inner . id {
625
658
write ! ( f, "{}://" , Self :: SCHEME . as_str( ) ) ?;
626
659
}
627
660
628
661
let mut needs_at = false ;
629
- if let Some ( username) = & self . username {
662
+ if let Some ( username) = & self . inner . username {
630
663
write ! ( f, "{username}" ) ?;
631
664
needs_at = true ;
632
665
}
633
666
634
- if let Some ( nonce) = self . nonce {
667
+ if let Some ( nonce) = self . nonce ( ) {
635
668
let timestamp = nonce. timestamp ( ) ;
636
669
write ! ( f, ":{timestamp}" ) ?;
637
670
needs_at = true ;
@@ -642,25 +675,25 @@ impl Display for CatalystId {
642
675
write ! ( f, "@" ) ?;
643
676
}
644
677
645
- if let Some ( subnet) = & self . subnet {
678
+ if let Some ( subnet) = & self . inner . subnet {
646
679
write ! ( f, "{subnet}." ) ?;
647
680
}
648
681
write ! (
649
682
f,
650
683
"{}/{}" ,
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( ) ) ,
653
686
) ?;
654
687
655
688
// 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) ?;
660
693
}
661
694
}
662
695
663
- if self . encryption {
696
+ if self . inner . encryption {
664
697
write ! ( f, "#{}" , Self :: ENCRYPTION_FRAGMENT ) ?;
665
698
}
666
699
Ok ( ( ) )
0 commit comments