Skip to content

Commit f49ab75

Browse files
feat(rust/catalyst-types): Wrap Catalyst ID fields into Arc (#421)
* Wrap Catalyst ID fields into Arc * Update documentation comment
1 parent 5c081f7 commit f49ab75

File tree

2 files changed

+80
-45
lines changed
  • rust
    • catalyst-types/src/catalyst_id
    • rbac-registration/src/registration/cardano

2 files changed

+80
-45
lines changed

rust/catalyst-types/src/catalyst_id/mod.rs

Lines changed: 78 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub mod role_index;
1010
use std::{
1111
fmt::{Display, Formatter},
1212
str::FromStr,
13+
sync::Arc,
1314
};
1415

1516
use 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)]
3539
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 {
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

622655
impl 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(())

rust/rbac-registration/src/registration/cardano/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ use crate::cardano::cip509::{
2929
};
3030

3131
/// Registration chains.
32+
///
33+
/// This structure uses [`Arc`] internally, so it is cheap to clone.
3234
#[derive(Debug, Clone)]
3335
pub struct RegistrationChain {
3436
/// Inner part of the registration chain.

0 commit comments

Comments
 (0)