diff --git a/packages/std/src/addresses.rs b/packages/std/src/addresses.rs index 953866db31..36052c90de 100644 --- a/packages/std/src/addresses.rs +++ b/packages/std/src/addresses.rs @@ -4,8 +4,10 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use std::fmt; use std::ops::Deref; +use std::str::from_utf8; use crate::binary::Binary; +use crate::errors::StdResult; /// A human readable address. /// @@ -45,6 +47,17 @@ impl Addr { pub fn unchecked>(input: T) -> Addr { Addr(input.into()) } + + #[inline] + pub fn as_str(&self) -> &str { + self.0.as_str() + } + + /// This gets an AddrRef, much like OwnedDeps.as_ref + /// We don't implement AsRef as we want an object back, not &AddrRef + pub fn as_ref(&'_ self) -> AddrRef<'_> { + AddrRef(self.as_str()) + } } impl fmt::Display for Addr { @@ -53,13 +66,6 @@ impl fmt::Display for Addr { } } -impl AsRef for Addr { - #[inline] - fn as_ref(&self) -> &str { - &self.0 - } -} - /// Implement `Addr == &str` impl PartialEq<&str> for Addr { fn eq(&self, rhs: &&str) -> bool { @@ -74,6 +80,12 @@ impl PartialEq for &str { } } +impl<'a> PartialEq> for Addr { + fn eq(&self, rhs: &AddrRef<'a>) -> bool { + self.0.as_str() == rhs.as_str() + } +} + /// Implement `Addr == String` impl PartialEq for Addr { fn eq(&self, rhs: &String) -> bool { @@ -103,15 +115,47 @@ impl From<&Addr> for String { } } -impl From for HumanAddr { - fn from(addr: Addr) -> Self { - HumanAddr(addr.0) +/// AddrRef is like &Addr but can be created easily without requiring a heap allocation +/// It is not designed to be serialized but used internally, especially working with storage-plus keys +#[derive(Debug, Clone, Copy)] +pub struct AddrRef<'a>(&'a str); + +impl<'a> AddrRef<'a> { + /// the safe way to construct one from an address (also via From/Into helpers) + pub fn new(addr: &'a Addr) -> Self { + AddrRef(addr.as_str()) + } + + /// This should only be used in test code + pub const fn unchecked(addr: &'a str) -> Self { + AddrRef(addr) + } + + /// This is for parsing raw keys stored in the db we created previously with Addr + pub fn unchecked_utf8(raw: &'a [u8]) -> StdResult { + Ok(AddrRef(from_utf8(raw)?)) + } + + pub fn as_str(&self) -> &str { + self.0 } } -impl From<&Addr> for HumanAddr { - fn from(addr: &Addr) -> Self { - HumanAddr(addr.0.clone()) +impl<'a> From<&'a Addr> for AddrRef<'a> { + fn from(addr: &'a Addr) -> Self { + AddrRef(addr.as_str()) + } +} + +impl<'a> PartialEq<&str> for AddrRef<'a> { + fn eq(&self, rhs: &&str) -> bool { + self.0 == *rhs + } +} + +impl<'a> From> for Addr { + fn from(addr: AddrRef<'a>) -> Self { + Addr(addr.0.to_string()) } } @@ -265,6 +309,11 @@ mod tests { use std::hash::{Hash, Hasher}; use std::iter::FromIterator; + // Let's see if we can get this directly from Addr + fn demo_deref(addr: AddrRef, compare: &str) { + assert_eq!(addr, compare); + } + #[test] fn addr_unchecked_works() { let a = Addr::unchecked("123"); @@ -288,6 +337,12 @@ mod tests { assert_eq!(addr.as_ref(), "literal-string"); } + #[test] + fn addr_as_str() { + let addr = Addr::unchecked("literal-string"); + assert_eq!(addr.as_str(), "literal-string"); + } + #[test] fn addr_implements_partial_eq_with_str() { let addr = Addr::unchecked("cos934gh9034hg04g0h134"); @@ -323,17 +378,35 @@ mod tests { } #[test] - fn addr_implements_into_human_address() { - // owned Addr - let addr = Addr::unchecked("cos934gh9034hg04g0h134"); - let human: HumanAddr = addr.into(); - assert_eq!(human, "cos934gh9034hg04g0h134"); + fn addr_ref_unchecked_from_literal() { + let addr_ref = AddrRef::unchecked("my-address"); + assert_eq!(addr_ref.as_str(), "my-address"); + // PartialEq also implemented + assert_eq!(addr_ref, "my-address"); + } - // &Addr - let addr = Addr::unchecked("cos934gh9034hg04g0h134"); - let addr_ref = &addr; - let human: HumanAddr = addr_ref.into(); - assert_eq!(human, "cos934gh9034hg04g0h134"); + #[test] + fn addr_ref_unchecked_from_bytes() { + let addr_ref = AddrRef::unchecked_utf8(b"some-text").unwrap(); + assert_eq!(addr_ref.as_str(), "some-text"); + // returns an error if we pass bad data in ("Invalid 2 octet sequence") + AddrRef::unchecked_utf8(&[0xc3, 0x28]).unwrap_err(); + } + + #[test] + fn addr_ref_to_addr() { + let addr_ref = AddrRef::unchecked("foobar"); + let addr: Addr = addr_ref.into(); + // same strings + assert_eq!(addr.as_str(), addr_ref.as_str()); + // helper also works + assert_eq!(addr, addr_ref); + } + + #[test] + fn addr_to_addr_ref() { + let addr = Addr::unchecked("some-long-string"); + demo_deref(addr.as_ref(), "some-long-string"); } // Test HumanAddr as_str() for each HumanAddr::from input type