use alloc::{format, string::String, vec::Vec};
use core::{
convert::TryFrom,
fmt::{self, Debug, Display, Formatter},
};
use hex_fmt::HexFmt;
use crate::{bytesrepr, AccessRights, ApiError, Key, ACCESS_RIGHTS_SERIALIZED_LENGTH};
pub const UREF_ADDR_LENGTH: usize = 32;
pub const UREF_SERIALIZED_LENGTH: usize = UREF_ADDR_LENGTH + ACCESS_RIGHTS_SERIALIZED_LENGTH;
pub type URefAddr = [u8; UREF_ADDR_LENGTH];
#[derive(Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct URef(URefAddr, AccessRights);
impl URef {
pub fn new(address: URefAddr, access_rights: AccessRights) -> Self {
URef(address, access_rights)
}
pub fn addr(&self) -> URefAddr {
self.0
}
pub fn access_rights(&self) -> AccessRights {
self.1
}
pub fn with_access_rights(self, access_rights: AccessRights) -> Self {
URef(self.0, access_rights)
}
pub fn remove_access_rights(self) -> Self {
URef(self.0, AccessRights::NONE)
}
pub fn is_readable(self) -> bool {
self.1.is_readable()
}
pub fn into_read(self) -> URef {
URef(self.0, AccessRights::READ)
}
pub fn into_read_add_write(self) -> URef {
URef(self.0, AccessRights::READ_ADD_WRITE)
}
pub fn is_writeable(self) -> bool {
self.1.is_writeable()
}
pub fn is_addable(self) -> bool {
self.1.is_addable()
}
pub fn as_string(&self) -> String {
let access_rights_bits = self.access_rights().bits();
format!(
"uref-{}-{:03o}",
base16::encode_lower(&self.addr()),
access_rights_bits
)
}
}
impl Display for URef {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let addr = self.addr();
let access_rights = self.access_rights();
write!(f, "URef({}, {})", HexFmt(&addr), access_rights)
}
}
impl Debug for URef {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self)
}
}
impl bytesrepr::ToBytes for URef {
fn to_bytes(&self) -> Result<Vec<u8>, bytesrepr::Error> {
let mut result = bytesrepr::unchecked_allocate_buffer(self);
result.append(&mut self.0.to_bytes()?);
result.append(&mut self.1.to_bytes()?);
Ok(result)
}
fn serialized_length(&self) -> usize {
UREF_SERIALIZED_LENGTH
}
}
impl bytesrepr::FromBytes for URef {
fn from_bytes(bytes: &[u8]) -> Result<(Self, &[u8]), bytesrepr::Error> {
let (id, rem): ([u8; 32], &[u8]) = bytesrepr::FromBytes::from_bytes(bytes)?;
let (access_rights, rem): (AccessRights, &[u8]) = bytesrepr::FromBytes::from_bytes(rem)?;
Ok((URef(id, access_rights), rem))
}
}
impl TryFrom<Key> for URef {
type Error = ApiError;
fn try_from(key: Key) -> Result<Self, Self::Error> {
if let Key::URef(uref) = key {
Ok(uref)
} else {
Err(ApiError::UnexpectedKeyVariant)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uref_as_string() {
let addr_array = [0u8; 32];
let uref_a = URef::new(addr_array, AccessRights::READ);
assert_eq!(
uref_a.as_string(),
"uref-0000000000000000000000000000000000000000000000000000000000000000-001"
);
let uref_b = URef::new(addr_array, AccessRights::WRITE);
assert_eq!(
uref_b.as_string(),
"uref-0000000000000000000000000000000000000000000000000000000000000000-002"
);
let uref_c = uref_b.remove_access_rights();
assert_eq!(
uref_c.as_string(),
"uref-0000000000000000000000000000000000000000000000000000000000000000-000"
);
}
}