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