upend/base/src/addressing.rs

244 lines
7.4 KiB
Rust
Raw Normal View History

2023-06-25 15:29:52 +02:00
use crate::hash::{b58_decode, b58_encode, AsDigest, Digest};
use anyhow::{anyhow, Result};
use serde::de::Visitor;
use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
2023-06-25 15:29:52 +02:00
use std::hash::Hash;
use std::str::FromStr;
use url::Url;
use uuid::Uuid;
2020-09-07 21:21:54 +02:00
2021-12-04 18:33:40 +01:00
#[derive(Clone, Eq, PartialEq, Hash)]
2020-09-07 21:21:54 +02:00
pub enum Address {
2023-06-25 15:29:52 +02:00
Hash(Digest),
2021-04-24 00:08:17 +02:00
Uuid(Uuid),
Attribute(String),
Url(Url),
2020-09-07 21:21:54 +02:00
}
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
pub struct AddressComponents<'a> {
pub t: &'a str,
pub c: Option<&'a str>,
}
// multihash SHA2-256
const SHA2_256: u64 = 0x12;
2020-09-07 21:21:54 +02:00
// multihash identity
const IDENTITY: u64 = 0x00;
2020-09-07 21:21:54 +02:00
2023-06-25 15:29:52 +02:00
pub type LargeMultihash = multihash::MultihashGeneric<256>;
2020-09-07 21:21:54 +02:00
impl Address {
pub fn encode(&self) -> Result<Vec<u8>> {
let hash = match self {
Self::Hash(hash) => {
LargeMultihash::wrap(SHA2_256, &hash.0).map_err(|err| anyhow!(err))?
}
Self::Uuid(uuid) => {
LargeMultihash::wrap(IDENTITY, &[vec![b'U'], uuid.as_bytes().to_vec()].concat())
.map_err(|err| anyhow!(err))?
}
Self::Attribute(attribute) => {
LargeMultihash::wrap(IDENTITY, &[&[b'A'], attribute.as_bytes()].concat())
.map_err(|err| anyhow!(err))?
}
Self::Url(url) => {
LargeMultihash::wrap(IDENTITY, &[&[b'X'], url.to_string().as_bytes()].concat())
.map_err(|err| anyhow!(err))?
}
2020-09-07 21:21:54 +02:00
};
Ok(hash.to_bytes())
2020-09-07 21:21:54 +02:00
}
2020-09-14 01:16:01 +02:00
pub fn decode(buffer: &[u8]) -> Result<Self> {
let multihash = LargeMultihash::from_bytes(buffer)
.map_err(|err| anyhow!("Error decoding address: {}", err))?;
match multihash.code() {
2023-06-25 15:29:52 +02:00
SHA2_256 => Ok(Self::Hash(Digest(multihash.digest().to_vec()))),
IDENTITY => {
let digest = multihash.digest().to_owned();
let digest_content: Vec<u8> = digest.clone().into_iter().skip(1).collect();
match digest[0] {
b'U' => Ok(Self::Uuid(uuid::Uuid::from_slice(
digest_content.as_slice(),
)?)),
b'A' => Ok(Self::Attribute(String::from_utf8(digest_content)?)),
b'X' => Ok(Self::Url(Url::parse(&String::from_utf8(digest_content)?)?)),
_ => Err(anyhow!("Error decoding address: Unknown identity marker.")),
}
2020-09-07 21:21:54 +02:00
}
_ => Err(anyhow!(
"Error decoding address: Unknown hash function type."
)),
2020-09-07 21:21:54 +02:00
}
}
pub fn as_components(&self) -> AddressComponents {
// TODO: make this automatically derive from `Address` definition
let (entity_type, entity_content) = match self {
Address::Hash(_) => ("Hash", None),
Address::Uuid(_) => ("Uuid", None),
Address::Attribute(attribute) => ("Attribute", Some(attribute.as_str())),
Address::Url(url) => ("Url", Some(url.as_str())),
};
AddressComponents {
t: entity_type,
c: entity_content,
}
}
pub fn from_components(components: &AddressComponents) -> Result<Self> {
// TODO: make this automatically derive from `Address` definition
let address = match *components {
AddressComponents { t: "Attribute", c } => Address::Attribute(
c.map(|x| x.to_string())
.ok_or(anyhow!("Missing attribute."))?,
),
AddressComponents { t: "Url", c } => Address::Url(if let Some(string) = c {
Url::parse(&string)?
} else {
Err(anyhow!("Missing URL."))?
}),
AddressComponents { t: "Uuid", c } => match c {
Some(c) => c.parse()?,
None => Address::Uuid(Uuid::new_v4()),
},
AddressComponents { t, .. } => Err(anyhow!("Unknown attribute: \"{}\"", t))?,
};
Ok(address)
}
2020-09-07 21:21:54 +02:00
}
impl Serialize for Address {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
2022-01-26 16:55:23 +01:00
serializer.serialize_str(b58_encode(self.encode().map_err(ser::Error::custom)?).as_str())
}
}
struct AddressVisitor;
impl<'de> Visitor<'de> for AddressVisitor {
type Value = Address;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid UpEnd address (hash/UUID) as a multi-hashed string")
}
fn visit_str<E>(self, str: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = b58_decode(str)
.map_err(|e| de::Error::custom(format!("Error deserializing address: {}", e)))?;
Address::decode(bytes.as_ref())
.map_err(|e| de::Error::custom(format!("Error deserializing address: {}", e)))
}
}
impl<'de> Deserialize<'de> for Address {
fn deserialize<D>(deserializer: D) -> Result<Address, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(AddressVisitor)
}
}
impl FromStr for Address {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Address::decode(
b58_decode(s)
.map_err(|e| anyhow!("Error deserializing address: {}", e))?
.as_ref(),
)
}
}
2020-09-07 21:21:54 +02:00
impl std::fmt::Display for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2022-01-27 10:51:26 +01:00
write!(
f,
"{}",
b58_encode(self.encode().map_err(|_| std::fmt::Error)?)
)
2020-09-07 21:21:54 +02:00
}
}
impl std::fmt::Debug for Address {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Address<{}>: {}",
match self {
Address::Hash(_) => "Hash",
Address::Uuid(_) => "UUID",
Address::Attribute(_) => "Attribute",
Address::Url(_) => "URL",
},
self
)
}
}
2023-06-25 15:29:52 +02:00
pub trait Addressable: AsDigest {
fn address(&self) -> Result<Address> {
2023-06-25 15:29:52 +02:00
Ok(Address::Hash(self.as_digest()?))
}
}
2020-09-07 21:21:54 +02:00
#[cfg(test)]
mod tests {
2022-01-27 10:53:19 +01:00
use anyhow::Result;
use url::Url;
2020-09-07 21:21:54 +02:00
use uuid::Uuid;
use crate::addressing::Address;
2023-06-25 15:29:52 +02:00
use crate::hash::Digest;
2020-09-07 21:21:54 +02:00
#[test]
2022-01-27 10:53:19 +01:00
fn test_hash_codec() -> Result<()> {
2023-06-25 15:29:52 +02:00
let addr = Address::Hash(Digest(vec![1, 2, 3, 4, 5]));
2022-01-27 10:53:19 +01:00
let encoded = addr.encode()?;
let decoded = Address::decode(&encoded)?;
assert_eq!(addr, decoded);
Ok(())
2020-09-07 21:21:54 +02:00
}
#[test]
2022-01-27 10:53:19 +01:00
fn test_uuid_codec() -> Result<()> {
2021-04-24 00:08:17 +02:00
let addr = Address::Uuid(Uuid::new_v4());
2022-01-27 10:53:19 +01:00
let encoded = addr.encode()?;
let decoded = Address::decode(&encoded)?;
assert_eq!(addr, decoded);
Ok(())
2020-09-07 21:21:54 +02:00
}
#[test]
2022-01-27 10:53:19 +01:00
fn test_attribute_codec() -> Result<()> {
let addr = Address::Attribute(String::from("ATTRIBUTE"));
2022-01-27 10:53:19 +01:00
let encoded = addr.encode()?;
let decoded = Address::decode(&encoded)?;
assert_eq!(addr, decoded);
Ok(())
}
2022-01-27 10:51:26 +01:00
#[test]
2022-01-27 10:53:19 +01:00
fn test_url_codec() -> Result<()> {
let addr = Address::Url(Url::parse("https://upend.dev/an/url/that/is/particularly/long/because/multihash/used/to/have/a/small/limit").unwrap());
2022-01-27 10:53:19 +01:00
let encoded = addr.encode()?;
let decoded = Address::decode(&encoded)?;
assert_eq!(addr, decoded);
Ok(())
2022-01-27 10:51:26 +01:00
}
2020-09-07 21:21:54 +02:00
}