use crate::{cas::storage::ContentAddressableStorage, hash::HashString};
use holochain_json_api::{error::JsonError, json::*};
use multihash::Hash;
use std::fmt::{Debug, Write};
pub type Address = HashString;
pub type Content = JsonString;
pub trait AddressableContent {
fn address(&self) -> Address {
Address::encode_from_str(&String::from(self.content()), Hash::SHA2256)
}
fn content(&self) -> Content;
fn try_from_content(content: &Content) -> Result<Self, JsonError>
where
Self: Sized;
}
impl AddressableContent for Content {
fn content(&self) -> Content {
self.clone()
}
fn try_from_content(content: &Content) -> Result<Self, JsonError> {
Ok(content.clone())
}
}
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
pub struct ExampleAddressableContent {
content: Content,
}
impl AddressableContent for ExampleAddressableContent {
fn content(&self) -> Content {
self.content.clone()
}
fn try_from_content(content: &Content) -> Result<Self, JsonError> {
Ok(ExampleAddressableContent {
content: content.clone(),
})
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct OtherExampleAddressableContent {
content: Content,
address: Address,
}
impl AddressableContent for OtherExampleAddressableContent {
fn address(&self) -> Address {
self.address.clone()
}
fn content(&self) -> Content {
self.content.clone()
}
fn try_from_content(content: &Content) -> Result<Self, JsonError> {
Ok(OtherExampleAddressableContent {
content: content.clone(),
address: Address::encode_from_str(&String::from(content), Hash::SHA2256),
})
}
}
pub struct AddressableContentTestSuite;
impl AddressableContentTestSuite {
pub fn addressable_content_trait_test<T>(
content: Content,
expected_content: T,
address: Address,
) where
T: AddressableContent + Debug + PartialEq + Clone,
{
let addressable_content = T::try_from_content(&content)
.expect("could not create AddressableContent from Content");
assert_eq!(addressable_content, expected_content);
assert_eq!(content, addressable_content.content());
assert_eq!(address, addressable_content.address());
}
pub fn addressable_contents_are_the_same_test<T, K>(content: Content)
where
T: AddressableContent + Debug + PartialEq + Clone,
K: AddressableContent + Debug + PartialEq + Clone,
{
let addressable_content = T::try_from_content(&content)
.expect("could not create AddressableContent from Content");
let other_addressable_content = K::try_from_content(&content)
.expect("could not create AddressableContent from Content");
assert_eq!(
addressable_content.content(),
other_addressable_content.content()
);
assert_eq!(
addressable_content.address(),
other_addressable_content.address()
);
}
pub fn addressable_content_round_trip<T, K>(contents: Vec<T>, mut cas: K)
where
T: AddressableContent + PartialEq + Clone + Debug,
K: ContentAddressableStorage,
{
contents.into_iter().for_each(|f| {
let mut add_error_message = String::new();
let mut fetch_error_message = String::new();
writeln!(&mut add_error_message, "Could not add {:?}", f.clone())
.expect("could not write");
writeln!(&mut fetch_error_message, "Could not fetch {:?}", f.clone())
.expect("could not write");
cas.add(&f).expect(&add_error_message);
assert_eq!(
Some(f.clone()),
Some(
T::try_from_content(
&cas.fetch(&f.address())
.expect(&fetch_error_message)
.expect("could not get json")
)
.unwrap()
)
);
});
}
}
#[cfg(test)]
pub mod tests {
use crate::cas::content::{
Address, AddressableContent, AddressableContentTestSuite, ExampleAddressableContent,
OtherExampleAddressableContent,
};
use holochain_json_api::json::{JsonString, RawString};
#[test]
fn example_addressable_content_trait_test() {
AddressableContentTestSuite::addressable_content_trait_test::<ExampleAddressableContent>(
JsonString::from(RawString::from("foo")),
ExampleAddressableContent::try_from_content(&JsonString::from(RawString::from("foo")))
.unwrap(),
Address::from("QmaKze4knhzQPuofhaXfg8kPG3V92MLgDX95xe8g5eafLn"),
);
}
#[test]
fn other_example_addressable_content_trait_test() {
AddressableContentTestSuite::addressable_content_trait_test::<OtherExampleAddressableContent>(
JsonString::from(RawString::from("foo")),
OtherExampleAddressableContent::try_from_content(&JsonString::from(RawString::from(
"foo",
)))
.unwrap(),
Address::from("QmaKze4knhzQPuofhaXfg8kPG3V92MLgDX95xe8g5eafLn"),
);
}
#[test]
fn example_addressable_contents_are_the_same_test() {
AddressableContentTestSuite::addressable_contents_are_the_same_test::<
ExampleAddressableContent,
OtherExampleAddressableContent,
>(JsonString::from(RawString::from("foo")));
}
}