1#![allow(clippy::field_reassign_with_default)]
3
4use alloc::{format, string::String, vec::Vec};
5use core::{
6 array::TryFromSliceError,
7 convert::TryFrom,
8 fmt::{self, Debug, Display, Formatter},
9 num::ParseIntError,
10};
11
12#[cfg(feature = "datasize")]
13use datasize::DataSize;
14use rand::{
15 distributions::{Distribution, Standard},
16 Rng,
17};
18#[cfg(feature = "json-schema")]
19use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
20use serde::{de::Error as SerdeError, Deserialize, Deserializer, Serialize, Serializer};
21
22use crate::{
23 bytesrepr,
24 bytesrepr::{Error, FromBytes},
25 checksummed_hex, AccessRights, ApiError, Key, ACCESS_RIGHTS_SERIALIZED_LENGTH,
26};
27
28pub const UREF_ADDR_LENGTH: usize = 32;
30
31pub const UREF_SERIALIZED_LENGTH: usize = UREF_ADDR_LENGTH + ACCESS_RIGHTS_SERIALIZED_LENGTH;
33
34pub(super) const UREF_FORMATTED_STRING_PREFIX: &str = "uref-";
35
36pub type URefAddr = [u8; UREF_ADDR_LENGTH];
38
39#[derive(Debug)]
41#[non_exhaustive]
42pub enum FromStrError {
43 InvalidPrefix,
45 MissingSuffix,
47 InvalidAccessRights,
49 Hex(base16::DecodeError),
51 Int(ParseIntError),
53 Address(TryFromSliceError),
55}
56
57impl From<base16::DecodeError> for FromStrError {
58 fn from(error: base16::DecodeError) -> Self {
59 FromStrError::Hex(error)
60 }
61}
62
63impl From<ParseIntError> for FromStrError {
64 fn from(error: ParseIntError) -> Self {
65 FromStrError::Int(error)
66 }
67}
68
69impl From<TryFromSliceError> for FromStrError {
70 fn from(error: TryFromSliceError) -> Self {
71 FromStrError::Address(error)
72 }
73}
74
75impl Display for FromStrError {
76 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
77 match self {
78 FromStrError::InvalidPrefix => write!(f, "prefix is not 'uref-'"),
79 FromStrError::MissingSuffix => write!(f, "no access rights as suffix"),
80 FromStrError::InvalidAccessRights => write!(f, "invalid access rights"),
81 FromStrError::Hex(error) => {
82 write!(f, "failed to decode address portion from hex: {}", error)
83 }
84 FromStrError::Int(error) => write!(f, "failed to parse an int: {}", error),
85 FromStrError::Address(error) => {
86 write!(f, "address portion is the wrong length: {}", error)
87 }
88 }
89 }
90}
91
92#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
97#[cfg_attr(feature = "datasize", derive(DataSize))]
98pub struct URef(URefAddr, AccessRights);
99
100impl URef {
101 pub const fn new(address: URefAddr, access_rights: AccessRights) -> Self {
103 URef(address, access_rights)
104 }
105
106 pub fn addr(&self) -> URefAddr {
108 self.0
109 }
110
111 pub fn access_rights(&self) -> AccessRights {
113 self.1
114 }
115
116 #[must_use]
118 pub fn with_access_rights(self, access_rights: AccessRights) -> Self {
119 URef(self.0, access_rights)
120 }
121
122 #[must_use]
124 pub fn remove_access_rights(self) -> Self {
125 URef(self.0, AccessRights::NONE)
126 }
127
128 #[must_use]
131 pub fn is_readable(self) -> bool {
132 self.1.is_readable()
133 }
134
135 #[must_use]
137 pub fn into_read(self) -> URef {
138 URef(self.0, AccessRights::READ)
139 }
140
141 #[must_use]
143 pub fn into_write(self) -> URef {
144 URef(self.0, AccessRights::WRITE)
145 }
146
147 #[must_use]
149 pub fn into_add(self) -> URef {
150 URef(self.0, AccessRights::ADD)
151 }
152
153 #[must_use]
156 pub fn into_read_add_write(self) -> URef {
157 URef(self.0, AccessRights::READ_ADD_WRITE)
158 }
159
160 #[must_use]
163 pub fn into_read_write(self) -> URef {
164 URef(self.0, AccessRights::READ_WRITE)
165 }
166
167 pub fn is_writeable(self) -> bool {
170 self.1.is_writeable()
171 }
172
173 pub fn is_addable(self) -> bool {
176 self.1.is_addable()
177 }
178
179 pub fn to_formatted_string(self) -> String {
182 let access_rights_bits = self.access_rights().bits();
184 format!(
187 "{}{}-{:03o}",
188 UREF_FORMATTED_STRING_PREFIX,
189 base16::encode_lower(&self.addr()),
190 access_rights_bits
191 )
192 }
193
194 pub fn from_formatted_str(input: &str) -> Result<Self, FromStrError> {
196 let remainder = input
197 .strip_prefix(UREF_FORMATTED_STRING_PREFIX)
198 .ok_or(FromStrError::InvalidPrefix)?;
199 let parts = remainder.splitn(2, '-').collect::<Vec<_>>();
200 if parts.len() != 2 {
201 return Err(FromStrError::MissingSuffix);
202 }
203 let addr = URefAddr::try_from(checksummed_hex::decode(parts[0])?.as_ref())?;
204 let access_rights_value = u8::from_str_radix(parts[1], 8)?;
205 let access_rights = AccessRights::from_bits(access_rights_value)
206 .ok_or(FromStrError::InvalidAccessRights)?;
207 Ok(URef(addr, access_rights))
208 }
209
210 pub fn disable_access_rights(&mut self, access_rights: AccessRights) {
212 self.1.remove(access_rights)
213 }
214}
215
216#[cfg(feature = "json-schema")]
217impl JsonSchema for URef {
218 fn schema_name() -> String {
219 String::from("URef")
220 }
221
222 fn json_schema(gen: &mut SchemaGenerator) -> Schema {
223 let schema = gen.subschema_for::<String>();
224 let mut schema_object = schema.into_object();
225 schema_object.metadata().description = Some(String::from("Hex-encoded, formatted URef."));
226 schema_object.into()
227 }
228}
229
230impl Display for URef {
231 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
232 let addr = self.addr();
233 let access_rights = self.access_rights();
234 write!(
235 f,
236 "URef({}, {})",
237 base16::encode_lower(&addr),
238 access_rights
239 )
240 }
241}
242
243impl Debug for URef {
244 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
245 write!(f, "{}", self)
246 }
247}
248
249impl bytesrepr::ToBytes for URef {
250 fn to_bytes(&self) -> Result<Vec<u8>, Error> {
251 let mut result = bytesrepr::unchecked_allocate_buffer(self);
252 result.append(&mut self.0.to_bytes()?);
253 result.append(&mut self.1.to_bytes()?);
254 Ok(result)
255 }
256
257 fn serialized_length(&self) -> usize {
258 UREF_SERIALIZED_LENGTH
259 }
260
261 fn write_bytes(&self, writer: &mut Vec<u8>) -> Result<(), self::Error> {
262 writer.extend_from_slice(&self.0);
263 self.1.write_bytes(writer)?;
264 Ok(())
265 }
266}
267
268impl FromBytes for URef {
269 fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), Error> {
270 let (id, rem) = FromBytes::from_bytes(bytes)?;
271 let (access_rights, rem) = FromBytes::from_bytes(rem)?;
272 Ok((URef(id, access_rights), rem))
273 }
274}
275
276impl Serialize for URef {
277 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
278 if serializer.is_human_readable() {
279 self.to_formatted_string().serialize(serializer)
280 } else {
281 (self.0, self.1).serialize(serializer)
282 }
283 }
284}
285
286impl<'de> Deserialize<'de> for URef {
287 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
288 if deserializer.is_human_readable() {
289 let formatted_string = String::deserialize(deserializer)?;
290 URef::from_formatted_str(&formatted_string).map_err(D::Error::custom)
291 } else {
292 let (address, access_rights) = <(URefAddr, AccessRights)>::deserialize(deserializer)?;
293 Ok(URef(address, access_rights))
294 }
295 }
296}
297
298impl TryFrom<Key> for URef {
299 type Error = ApiError;
300
301 fn try_from(key: Key) -> Result<Self, Self::Error> {
302 if let Key::URef(uref) = key {
303 Ok(uref)
304 } else {
305 Err(ApiError::UnexpectedKeyVariant)
306 }
307 }
308}
309
310impl Distribution<URef> for Standard {
311 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> URef {
312 URef::new(rng.gen(), rng.gen())
313 }
314}
315
316#[cfg(test)]
317mod tests {
318 use super::*;
319
320 #[test]
321 fn uref_as_string() {
322 let addr_array = [0u8; 32];
326 let uref_a = URef::new(addr_array, AccessRights::READ);
327 assert_eq!(
328 uref_a.to_formatted_string(),
329 "uref-0000000000000000000000000000000000000000000000000000000000000000-001"
330 );
331 let uref_b = URef::new(addr_array, AccessRights::WRITE);
332 assert_eq!(
333 uref_b.to_formatted_string(),
334 "uref-0000000000000000000000000000000000000000000000000000000000000000-002"
335 );
336
337 let uref_c = uref_b.remove_access_rights();
338 assert_eq!(
339 uref_c.to_formatted_string(),
340 "uref-0000000000000000000000000000000000000000000000000000000000000000-000"
341 );
342 }
343
344 fn round_trip(uref: URef) {
345 let string = uref.to_formatted_string();
346 let parsed_uref = URef::from_formatted_str(&string).unwrap();
347 assert_eq!(uref, parsed_uref);
348 }
349
350 #[test]
351 fn uref_from_str() {
352 round_trip(URef::new([0; 32], AccessRights::NONE));
353 round_trip(URef::new([255; 32], AccessRights::READ_ADD_WRITE));
354
355 let invalid_prefix =
356 "ref-0000000000000000000000000000000000000000000000000000000000000000-000";
357 assert!(URef::from_formatted_str(invalid_prefix).is_err());
358
359 let invalid_prefix =
360 "uref0000000000000000000000000000000000000000000000000000000000000000-000";
361 assert!(URef::from_formatted_str(invalid_prefix).is_err());
362
363 let short_addr = "uref-00000000000000000000000000000000000000000000000000000000000000-000";
364 assert!(URef::from_formatted_str(short_addr).is_err());
365
366 let long_addr =
367 "uref-000000000000000000000000000000000000000000000000000000000000000000-000";
368 assert!(URef::from_formatted_str(long_addr).is_err());
369
370 let invalid_hex =
371 "uref-000000000000000000000000000000000000000000000000000000000000000g-000";
372 assert!(URef::from_formatted_str(invalid_hex).is_err());
373
374 let invalid_suffix_separator =
375 "uref-0000000000000000000000000000000000000000000000000000000000000000:000";
376 assert!(URef::from_formatted_str(invalid_suffix_separator).is_err());
377
378 let invalid_suffix =
379 "uref-0000000000000000000000000000000000000000000000000000000000000000-abc";
380 assert!(URef::from_formatted_str(invalid_suffix).is_err());
381
382 let invalid_access_rights =
383 "uref-0000000000000000000000000000000000000000000000000000000000000000-200";
384 assert!(URef::from_formatted_str(invalid_access_rights).is_err());
385 }
386
387 #[test]
388 fn serde_roundtrip() {
389 let uref = URef::new([255; 32], AccessRights::READ_ADD_WRITE);
390 let serialized = bincode::serialize(&uref).unwrap();
391 let decoded = bincode::deserialize(&serialized).unwrap();
392 assert_eq!(uref, decoded);
393 }
394
395 #[test]
396 fn json_roundtrip() {
397 let uref = URef::new([255; 32], AccessRights::READ_ADD_WRITE);
398 let json_string = serde_json::to_string_pretty(&uref).unwrap();
399 let decoded = serde_json::from_str(&json_string).unwrap();
400 assert_eq!(uref, decoded);
401 }
402
403 #[test]
404 fn should_disable_access_rights() {
405 let mut uref = URef::new([255; 32], AccessRights::READ_ADD_WRITE);
406 assert!(uref.is_writeable());
407 uref.disable_access_rights(AccessRights::WRITE);
408 assert_eq!(uref.access_rights(), AccessRights::READ_ADD);
409
410 uref.disable_access_rights(AccessRights::WRITE);
411 assert!(
412 !uref.is_writeable(),
413 "Disabling access bit twice should be a noop"
414 );
415
416 assert_eq!(uref.access_rights(), AccessRights::READ_ADD);
417
418 uref.disable_access_rights(AccessRights::READ_ADD);
419 assert_eq!(uref.access_rights(), AccessRights::NONE);
420
421 uref.disable_access_rights(AccessRights::READ_ADD);
422 assert_eq!(uref.access_rights(), AccessRights::NONE);
423
424 uref.disable_access_rights(AccessRights::NONE);
425 assert_eq!(uref.access_rights(), AccessRights::NONE);
426 }
427}