wip: get rid of anyhow in base, add wasm feature
parent
e5d645c7ee
commit
9f731d8ca0
|
@ -9,12 +9,11 @@ edition = "2018"
|
|||
|
||||
[features]
|
||||
diesel = []
|
||||
wasm = ["wasm-bindgen", "uuid/js"]
|
||||
|
||||
[dependencies]
|
||||
log = "0.4"
|
||||
|
||||
anyhow = "1.0"
|
||||
|
||||
lazy_static = "1.4.0"
|
||||
|
||||
diesel = { version = "1.4", features = ["sqlite"] }
|
||||
|
@ -30,10 +29,12 @@ multihash = { version = "*", default-features = false, features = [
|
|||
"sha2",
|
||||
"identity",
|
||||
] }
|
||||
uuid = { version = "0.8", features = ["v4", "serde"] }
|
||||
uuid = { version = "1.4", features = ["v4", "serde"] }
|
||||
url = { version = "2", features = ["serde"] }
|
||||
|
||||
nonempty = "0.6.0"
|
||||
|
||||
wasm-bindgen = { version = "0.2", optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
shadow-rs = "0.17"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::hash::{b58_decode, b58_encode, AsDigest, Digest};
|
||||
use anyhow::{anyhow, Result};
|
||||
use crate::error::{AddressComponentsDecodeError, UpEndError};
|
||||
use crate::hash::{b58_decode, b58_encode, AsDigest, AsDigestError, Digest};
|
||||
use serde::de::Visitor;
|
||||
use serde::{de, ser, Deserialize, Deserializer, Serialize, Serializer};
|
||||
use std::fmt;
|
||||
|
@ -8,6 +8,9 @@ use std::str::FromStr;
|
|||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub enum Address {
|
||||
Hash(Digest),
|
||||
|
@ -16,9 +19,10 @@ pub enum Address {
|
|||
Url(Url),
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "wasm", wasm_bindgen(getter_with_clone))]
|
||||
#[derive(Debug, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub struct AddressComponents<'a> {
|
||||
pub t: &'a str,
|
||||
pub struct AddressComponents {
|
||||
pub t: String,
|
||||
pub c: Option<String>,
|
||||
}
|
||||
|
||||
|
@ -30,31 +34,32 @@ const IDENTITY: u64 = 0x00;
|
|||
pub type LargeMultihash = multihash::MultihashGeneric<256>;
|
||||
|
||||
impl Address {
|
||||
pub fn encode(&self) -> Result<Vec<u8>> {
|
||||
pub fn encode(&self) -> Result<Vec<u8>, UpEndError> {
|
||||
let hash = match self {
|
||||
Self::Hash(hash) => {
|
||||
LargeMultihash::wrap(SHA2_256, &hash.0).map_err(|err| anyhow!(err))?
|
||||
LargeMultihash::wrap(SHA2_256, &hash.0).map_err(UpEndError::from_any)?
|
||||
}
|
||||
Self::Uuid(uuid) => {
|
||||
LargeMultihash::wrap(IDENTITY, &[vec![b'U'], uuid.as_bytes().to_vec()].concat())
|
||||
.map_err(|err| anyhow!(err))?
|
||||
.map_err(UpEndError::from_any)?
|
||||
}
|
||||
Self::Attribute(attribute) => {
|
||||
LargeMultihash::wrap(IDENTITY, &[&[b'A'], attribute.as_bytes()].concat())
|
||||
.map_err(|err| anyhow!(err))?
|
||||
.map_err(UpEndError::from_any)?
|
||||
}
|
||||
Self::Url(url) => {
|
||||
LargeMultihash::wrap(IDENTITY, &[&[b'X'], url.to_string().as_bytes()].concat())
|
||||
.map_err(|err| anyhow!(err))?
|
||||
.map_err(UpEndError::from_any)?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(hash.to_bytes())
|
||||
}
|
||||
|
||||
pub fn decode(buffer: &[u8]) -> Result<Self> {
|
||||
let multihash = LargeMultihash::from_bytes(buffer)
|
||||
.map_err(|err| anyhow!("Error decoding address: {}", err))?;
|
||||
pub fn decode(buffer: &[u8]) -> Result<Self, UpEndError> {
|
||||
let multihash = LargeMultihash::from_bytes(buffer).map_err(|err| {
|
||||
UpEndError::AddressParseError(format!("Error decoding address: {}", err))
|
||||
})?;
|
||||
|
||||
match multihash.code() {
|
||||
SHA2_256 => Ok(Self::Hash(Digest(multihash.digest().to_vec()))),
|
||||
|
@ -62,21 +67,31 @@ impl Address {
|
|||
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.")),
|
||||
b'U' => Ok(Self::Uuid(
|
||||
uuid::Uuid::from_slice(digest_content.as_slice())
|
||||
.map_err(UpEndError::from_any)?,
|
||||
)),
|
||||
b'A' => Ok(Self::Attribute(
|
||||
String::from_utf8(digest_content).map_err(UpEndError::from_any)?,
|
||||
)),
|
||||
b'X' => Ok(Self::Url(
|
||||
Url::parse(
|
||||
&String::from_utf8(digest_content).map_err(UpEndError::from_any)?,
|
||||
)
|
||||
.map_err(|e| UpEndError::AddressParseError(e.to_string()))?,
|
||||
)),
|
||||
_ => Err(UpEndError::AddressParseError(
|
||||
"Error decoding address: Unknown identity marker.".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
_ => Err(anyhow!(
|
||||
"Error decoding address: Unknown hash function type."
|
||||
_ => Err(UpEndError::AddressParseError(
|
||||
"Error decoding address: Unknown hash function type.".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_components<'a>(&'a self) -> AddressComponents<'a> {
|
||||
pub fn as_components<'a>(&'a self) -> AddressComponents {
|
||||
// TODO: make this automatically derive from `Address` definition
|
||||
let (entity_type, entity_content) = match self {
|
||||
Address::Hash(digest) => ("Hash", Some(b58_encode(digest))),
|
||||
|
@ -86,28 +101,38 @@ impl Address {
|
|||
};
|
||||
|
||||
AddressComponents {
|
||||
t: entity_type,
|
||||
t: entity_type.to_string(),
|
||||
c: entity_content,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_components(components: AddressComponents) -> Result<Self> {
|
||||
pub fn from_components(components: AddressComponents) -> Result<Self, UpEndError> {
|
||||
// TODO: make this automatically derive from `Address` definition
|
||||
let address = match components {
|
||||
AddressComponents { t: "Attribute", c } => Address::Attribute(
|
||||
AddressComponents { t, c } if t == "Attribute" => Address::Attribute(
|
||||
c.map(|x| x.to_string())
|
||||
.ok_or(anyhow!("Missing attribute."))?,
|
||||
.ok_or(UpEndError::AddressComponentsDecodeError(
|
||||
AddressComponentsDecodeError::MissingValue,
|
||||
))?,
|
||||
),
|
||||
AddressComponents { t: "Url", c } => Address::Url(if let Some(string) = c {
|
||||
Url::parse(&string)?
|
||||
AddressComponents { t, c } if t == "Url" => Address::Url(if let Some(string) = c {
|
||||
Url::parse(&string).map_err(|e| {
|
||||
UpEndError::AddressComponentsDecodeError(
|
||||
AddressComponentsDecodeError::UrlDecodeError(e.to_string()),
|
||||
)
|
||||
})?
|
||||
} else {
|
||||
Err(anyhow!("Missing URL."))?
|
||||
Err(UpEndError::AddressComponentsDecodeError(
|
||||
AddressComponentsDecodeError::MissingValue,
|
||||
))?
|
||||
}),
|
||||
AddressComponents { t: "Uuid", c } => match c {
|
||||
AddressComponents { t, c } if t == "Uuid" => match c {
|
||||
Some(c) => c.parse()?,
|
||||
None => Address::Uuid(Uuid::new_v4()),
|
||||
},
|
||||
AddressComponents { t, .. } => Err(anyhow!("Unknown attribute: \"{}\"", t))?,
|
||||
AddressComponents { t, .. } => Err(UpEndError::AddressComponentsDecodeError(
|
||||
AddressComponentsDecodeError::UnknownType(t),
|
||||
))?,
|
||||
};
|
||||
|
||||
Ok(address)
|
||||
|
@ -153,12 +178,14 @@ impl<'de> Deserialize<'de> for Address {
|
|||
}
|
||||
|
||||
impl FromStr for Address {
|
||||
type Err = anyhow::Error;
|
||||
type Err = UpEndError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Address::decode(
|
||||
b58_decode(s)
|
||||
.map_err(|e| anyhow!("Error deserializing address: {}", e))?
|
||||
.map_err(|e| {
|
||||
UpEndError::HashDecodeError(format!("Error deserializing address: {}", e))
|
||||
})?
|
||||
.as_ref(),
|
||||
)
|
||||
}
|
||||
|
@ -191,22 +218,23 @@ impl std::fmt::Debug for Address {
|
|||
}
|
||||
|
||||
pub trait Addressable: AsDigest {
|
||||
fn address(&self) -> Result<Address> {
|
||||
fn address(&self) -> Result<Address, AsDigestError> {
|
||||
Ok(Address::Hash(self.as_digest()?))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use anyhow::Result;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::addressing::Address;
|
||||
use crate::hash::Digest;
|
||||
|
||||
use super::UpEndError;
|
||||
|
||||
#[test]
|
||||
fn test_hash_codec() -> Result<()> {
|
||||
fn test_hash_codec() -> Result<(), UpEndError> {
|
||||
let addr = Address::Hash(Digest(vec![1, 2, 3, 4, 5]));
|
||||
let encoded = addr.encode()?;
|
||||
let decoded = Address::decode(&encoded)?;
|
||||
|
@ -215,7 +243,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_uuid_codec() -> Result<()> {
|
||||
fn test_uuid_codec() -> Result<(), UpEndError> {
|
||||
let addr = Address::Uuid(Uuid::new_v4());
|
||||
let encoded = addr.encode()?;
|
||||
let decoded = Address::decode(&encoded)?;
|
||||
|
@ -224,7 +252,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_attribute_codec() -> Result<()> {
|
||||
fn test_attribute_codec() -> Result<(), UpEndError> {
|
||||
let addr = Address::Attribute(String::from("ATTRIBUTE"));
|
||||
let encoded = addr.encode()?;
|
||||
let decoded = Address::decode(&encoded)?;
|
||||
|
@ -233,7 +261,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_url_codec() -> Result<()> {
|
||||
fn test_url_codec() -> Result<(), UpEndError> {
|
||||
let addr = Address::Url(Url::parse("https://upend.dev/an/url/that/is/particularly/long/because/multihash/used/to/have/a/small/limit").unwrap());
|
||||
let encoded = addr.encode()?;
|
||||
let decoded = Address::decode(&encoded)?;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::addressing::{Address, Addressable};
|
||||
use crate::error::UpEndError;
|
||||
use crate::hash::{b58_decode, sha256hash, AsDigest, AsDigestError, Digest};
|
||||
use anyhow::{anyhow, Result};
|
||||
use chrono::NaiveDateTime;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
|
@ -36,7 +36,7 @@ pub enum EntryValue {
|
|||
}
|
||||
|
||||
impl TryFrom<&InvariantEntry> for Entry {
|
||||
type Error = anyhow::Error;
|
||||
type Error = UpEndError;
|
||||
|
||||
fn try_from(invariant: &InvariantEntry) -> Result<Self, Self::Error> {
|
||||
Ok(Entry {
|
||||
|
@ -50,10 +50,14 @@ impl TryFrom<&InvariantEntry> for Entry {
|
|||
}
|
||||
|
||||
impl InvariantEntry {
|
||||
pub fn entity(&self) -> Result<Address> {
|
||||
pub fn entity(&self) -> Result<Address, UpEndError> {
|
||||
let mut entity = Cursor::new(vec![0u8; 0]);
|
||||
entity.write_all(self.attribute.as_bytes())?;
|
||||
entity.write_all(self.value.to_string()?.as_bytes())?;
|
||||
entity
|
||||
.write_all(self.attribute.as_bytes())
|
||||
.map_err(UpEndError::from_any)?;
|
||||
entity
|
||||
.write_all(self.value.to_string()?.as_bytes())
|
||||
.map_err(UpEndError::from_any)?;
|
||||
Ok(Address::Hash(sha256hash(entity.into_inner())))
|
||||
}
|
||||
}
|
||||
|
@ -112,13 +116,13 @@ impl Addressable for Entry {}
|
|||
impl Addressable for InvariantEntry {}
|
||||
|
||||
impl EntryValue {
|
||||
pub fn to_string(&self) -> Result<String> {
|
||||
pub fn to_string(&self) -> Result<String, UpEndError> {
|
||||
let (type_char, content) = match self {
|
||||
EntryValue::String(value) => ('S', value.to_owned()),
|
||||
EntryValue::Number(n) => ('N', n.to_string()),
|
||||
EntryValue::Address(address) => ('O', address.to_string()),
|
||||
EntryValue::Null => ('X', "".to_string()),
|
||||
EntryValue::Invalid => return Err(anyhow!("Cannot serialize invalid value.")),
|
||||
EntryValue::Invalid => return Err(UpEndError::CannotSerializeInvalid),
|
||||
};
|
||||
|
||||
Ok(format!("{}{}", type_char, content))
|
||||
|
@ -219,33 +223,32 @@ impl From<Address> for EntryValue {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
|
||||
#[test]
|
||||
fn test_value_from_to_string() -> Result<()> {
|
||||
fn test_value_from_to_string() -> Result<(), UpEndError> {
|
||||
let entry = EntryValue::String("hello".to_string());
|
||||
let encoded = entry.to_string()?;
|
||||
let decoded = encoded.parse::<EntryValue>()?;
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Number(1337.93);
|
||||
let encoded = entry.to_string()?;
|
||||
let decoded = encoded.parse::<EntryValue>()?;
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Address(Address::Url(Url::parse("https://upend.dev").unwrap()));
|
||||
let encoded = entry.to_string()?;
|
||||
let decoded = encoded.parse::<EntryValue>()?;
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::String("".to_string());
|
||||
let encoded = entry.to_string()?;
|
||||
let decoded = encoded.parse::<EntryValue>()?;
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
let entry = EntryValue::Null;
|
||||
let encoded = entry.to_string()?;
|
||||
let decoded = encoded.parse::<EntryValue>()?;
|
||||
let decoded = encoded.parse::<EntryValue>().unwrap();
|
||||
assert_eq!(entry, decoded);
|
||||
|
||||
Ok(())
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum UpEndError {
|
||||
HashDecodeError(String),
|
||||
AddressParseError(String),
|
||||
AddressComponentsDecodeError(AddressComponentsDecodeError),
|
||||
CannotSerializeInvalid,
|
||||
Other(String),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum AddressComponentsDecodeError {
|
||||
UnknownType(String),
|
||||
UrlDecodeError(String),
|
||||
MissingValue,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for UpEndError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
UpEndError::HashDecodeError(err) => format!("Could not decode hash: {err}"),
|
||||
UpEndError::AddressParseError(err) => format!("Error parsing address: {err}"),
|
||||
UpEndError::AddressComponentsDecodeError(cde) => match cde {
|
||||
AddressComponentsDecodeError::UnknownType(t) =>
|
||||
format!("Unknown type: \"{t}\""),
|
||||
AddressComponentsDecodeError::MissingValue =>
|
||||
String::from("Address type requires a value."),
|
||||
AddressComponentsDecodeError::UrlDecodeError(err) =>
|
||||
format!("Couldn't decode URL: {err}"),
|
||||
},
|
||||
UpEndError::CannotSerializeInvalid =>
|
||||
String::from("Invalid EntryValues cannot be serialized."),
|
||||
UpEndError::Other(err) => format!("Unknown error: {err}"),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for UpEndError {}
|
||||
|
||||
impl UpEndError {
|
||||
pub fn from_any<E: std::fmt::Display>(error: E) -> Self {
|
||||
UpEndError::Other(error.to_string())
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
use crate::addressing::Address;
|
||||
use anyhow::Result;
|
||||
use crate::{addressing::Address, error::UpEndError};
|
||||
use multihash::Hasher;
|
||||
use serde::{ser, Serialize, Serializer};
|
||||
|
||||
|
@ -49,9 +48,10 @@ pub fn b58_encode<T: AsRef<[u8]>>(vec: T) -> String {
|
|||
multibase::encode(multibase::Base::Base58Btc, vec.as_ref())
|
||||
}
|
||||
|
||||
pub fn b58_decode<T: AsRef<str>>(input: T) -> Result<Vec<u8>> {
|
||||
pub fn b58_decode<T: AsRef<str>>(input: T) -> Result<Vec<u8>, UpEndError> {
|
||||
let input = input.as_ref();
|
||||
let (_base, data) = multibase::decode(input)?;
|
||||
let (_base, data) =
|
||||
multibase::decode(input).map_err(|err| UpEndError::HashDecodeError(err.to_string()))?;
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::addressing::Address;
|
||||
use crate::entry::EntryValue;
|
||||
use crate::error::UpEndError;
|
||||
use nonempty::NonEmpty;
|
||||
use std::borrow::Borrow;
|
||||
use std::convert::TryFrom;
|
||||
|
@ -41,7 +42,7 @@ impl TryFrom<lexpr::Value> for Address {
|
|||
if let Some(address_str) = str.strip_prefix('@') {
|
||||
address_str
|
||||
.parse()
|
||||
.map_err(|e: anyhow::Error| QueryParseError(e.to_string()))
|
||||
.map_err(|e: UpEndError| QueryParseError(e.to_string()))
|
||||
} else {
|
||||
Err(QueryParseError(
|
||||
"Incorrect address format (use @address).".into(),
|
||||
|
@ -307,12 +308,13 @@ impl FromStr for Query {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::error::UpEndError;
|
||||
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
use url::Url;
|
||||
|
||||
#[test]
|
||||
fn test_matches() -> Result<()> {
|
||||
fn test_matches() -> Result<(), UpEndError> {
|
||||
let query = "(matches ? ? ?)".parse::<Query>()?;
|
||||
assert_eq!(
|
||||
query,
|
||||
|
|
|
@ -5,4 +5,5 @@ pub mod addressing;
|
|||
pub mod constants;
|
||||
pub mod entry;
|
||||
pub mod hash;
|
||||
pub mod error;
|
||||
pub mod lang;
|
||||
|
|
Loading…
Reference in New Issue