use crate::hash_path::shard::ShardStrategy;
use crate::hash_path::shard::SHARDEND;
use crate::prelude::*;
use holochain_wasmer_guest::*;
use holochain_zome_types::link::LinkTag;
use std::str::FromStr;
use validate::RequiredValidationType;
pub const DELIMITER: &str = ".";
pub const NAME: [u8; 8] = [0x68, 0x64, 0x6b, 0x2e, 0x70, 0x61, 0x74, 0x68];
#[derive(Clone, PartialEq, Debug, Default, serde::Deserialize, serde::Serialize)]
#[repr(transparent)]
pub struct Component(#[serde(with = "serde_bytes")] Vec<u8>);
impl From<Vec<u8>> for Component {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
impl AsRef<[u8]> for Component {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<Component> for Vec<u8> {
fn from(component: Component) -> Self {
component.0
}
}
impl From<&str> for Component {
fn from(s: &str) -> Self {
let bytes: Vec<u8> = s
.chars()
.flat_map(|c| (c as u32).to_le_bytes().to_vec())
.collect();
Self::from(bytes)
}
}
impl From<&String> for Component {
fn from(s: &String) -> Self {
Self::from(s.as_str())
}
}
impl From<String> for Component {
fn from(s: String) -> Self {
Self::from(s.as_str())
}
}
impl TryFrom<&Component> for String {
type Error = SerializedBytesError;
fn try_from(component: &Component) -> Result<Self, Self::Error> {
if component.as_ref().len() % 4 != 0 {
return Err(SerializedBytesError::Deserialize(format!(
"attempted to create u32s from utf8 bytes of length not a factor of 4: length {}",
component.as_ref().len()
)));
}
let (chars, _, error) = component
.as_ref()
.iter()
.fold(
(vec![], vec![], None),
|(mut chars, mut build, mut error), b| {
if error.is_none() {
build.push(*b);
if build.len() == std::mem::size_of::<u32>() {
let le_bytes = build[0..std::mem::size_of::<u32>()].try_into().unwrap();
let u = u32::from_le_bytes(le_bytes);
match std::char::from_u32(u) {
Some(c) => {
chars.push(c);
build = vec![];
}
None => {
error = Some(Err(SerializedBytesError::Deserialize(format!(
"unknown char for u32: {}",
u
))));
}
}
}
}
(chars, build, error)
},
);
match error {
Some(error) => error,
None => Ok(chars.iter().collect::<String>()),
}
}
}
#[derive(
Clone, Debug, PartialEq, Default, serde::Deserialize, serde::Serialize, SerializedBytes,
)]
#[repr(transparent)]
pub struct Path(Vec<Component>);
entry_def!(Path EntryDef {
id: core::str::from_utf8(&NAME).unwrap().into(),
crdt_type: CrdtType,
required_validations: RequiredValidations::default(),
visibility: EntryVisibility::Public,
required_validation_type: RequiredValidationType::default(),
});
impl From<Vec<Component>> for Path {
fn from(components: Vec<Component>) -> Self {
Self(components)
}
}
impl From<Path> for Vec<Component> {
fn from(path: Path) -> Self {
path.0
}
}
impl AsRef<Vec<Component>> for Path {
fn as_ref(&self) -> &Vec<Component> {
self.0.as_ref()
}
}
impl From<&str> for Path {
fn from(s: &str) -> Self {
Self(
s.split(DELIMITER)
.filter(|s| !s.is_empty())
.flat_map(|s| match ShardStrategy::from_str(s) {
Ok(strategy) => {
let (_strategy, component) = s.split_at(s.find(SHARDEND).unwrap());
let component = component.trim_start_matches(SHARDEND);
let shard_path = Path::from((&strategy, component));
let mut shard_components: Vec<Component> = shard_path.into();
shard_components.push(Component::from(component));
shard_components
}
Err(_) => vec![Component::from(s)],
})
.collect(),
)
}
}
impl From<&String> for Path {
fn from(s: &String) -> Self {
Self::from(s.as_str())
}
}
impl From<String> for Path {
fn from(s: String) -> Self {
Self::from(s.as_str())
}
}
impl TryFrom<&Path> for LinkTag {
type Error = SerializedBytesError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
let path_bytes: Vec<u8> = UnsafeBytes::from(SerializedBytes::try_from(path)?).into();
let link_tag_bytes: Vec<u8> = NAME.iter().chain(path_bytes.iter()).cloned().collect();
Ok(LinkTag::new(link_tag_bytes))
}
}
impl TryFrom<&LinkTag> for Path {
type Error = SerializedBytesError;
fn try_from(link_tag: &LinkTag) -> Result<Self, Self::Error> {
let sb = SerializedBytes::from(UnsafeBytes::from(link_tag.as_ref()[NAME.len()..].to_vec()));
Ok(Self::try_from(sb)?)
}
}
impl Path {
pub fn hash(&self) -> ExternResult<holo_hash::EntryHash> {
hash_entry(Entry::try_from(self)?)
}
pub fn exists(&self) -> ExternResult<bool> {
Ok(get(self.hash()?, GetOptions::content())?.is_some())
}
pub fn ensure(&self) -> ExternResult<()> {
if !self.exists()? {
create_entry(self)?;
if let Some(parent) = self.parent() {
parent.ensure()?;
create_link(parent.hash()?, self.hash()?, LinkTag::try_from(self)?)?;
}
}
Ok(())
}
pub fn parent(&self) -> Option<Path> {
if self.as_ref().len() > 1 {
let parent_vec: Vec<Component> = self.as_ref()[0..self.as_ref().len() - 1].to_vec();
Some(parent_vec.into())
} else {
None
}
}
pub fn children(&self) -> ExternResult<holochain_zome_types::link::Links> {
Self::ensure(&self)?;
let links = get_links(
self.hash()?,
Some(holochain_zome_types::link::LinkTag::new(NAME)),
)?;
let mut unwrapped: Vec<holochain_zome_types::link::Link> = links.into_inner();
unwrapped.sort_unstable_by(|a, b| a.tag.cmp(&b.tag));
unwrapped.dedup_by(|a, b| a.tag.eq(&b.tag));
Ok(holochain_zome_types::link::Links::from(unwrapped))
}
pub fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails> {
Self::ensure(&self)?;
get_link_details(
self.hash()?,
Some(holochain_zome_types::link::LinkTag::new(NAME)),
)
}
}
#[test]
#[cfg(test)]
fn hash_path_delimiter() {
assert_eq!(".", DELIMITER,);
}
#[test]
#[cfg(test)]
fn hash_path_linktag() {
assert_eq!("hdk.path".as_bytes(), NAME);
let path = Path::from("foo.bar");
let link_tag = LinkTag::try_from(&path).unwrap();
assert_eq!(
&vec![
104, 100, 107, 46, 112, 97, 116, 104, 146, 196, 12, 102, 0, 0, 0, 111, 0, 0, 0, 111, 0,
0, 0, 196, 12, 98, 0, 0, 0, 97, 0, 0, 0, 114, 0, 0, 0
],
link_tag.as_ref(),
);
assert_eq!(Path::try_from(&link_tag).unwrap(), path,);
}
#[test]
#[cfg(test)]
fn hash_path_component() {
use ::fixt::prelude::*;
let bytes: Vec<u8> = U8Fixturator::new(Unpredictable).take(5).collect();
let component = Component::from(bytes.clone());
assert_eq!(bytes, component.as_ref(),);
assert_eq!(
Component::from(vec![102, 0, 0, 0, 111, 0, 0, 0, 111, 0, 0, 0]),
Component::from("foo"),
);
assert_eq!(
String::try_from(&Component::from(vec![
102, 0, 0, 0, 111, 0, 0, 0, 111, 0, 0, 0
]))
.unwrap(),
String::from("foo"),
);
assert_eq!(
String::try_from(&Component::from(vec![1])),
Err(SerializedBytesError::Deserialize(
"attempted to create u32s from utf8 bytes of length not a factor of 4: length 1".into()
)),
);
assert_eq!(
String::try_from(&Component::from(vec![9, 9, 9, 9])),
Err(SerializedBytesError::Deserialize(
"unknown char for u32: 151587081".into()
)),
);
}
#[test]
#[cfg(test)]
fn hash_path_path() {
use ::fixt::prelude::*;
let components: Vec<Component> = {
let mut vec = vec![];
for _ in 0..10 {
let bytes: Vec<u8> = U8Fixturator::new(Unpredictable).take(10).collect();
vec.push(Component::from(bytes))
}
vec
};
assert_eq!(&components, Path::from(components.clone()).as_ref(),);
for (input, output) in vec![
("", vec![]),
(".", vec![]),
(".foo", vec![Component::from("foo")]),
("foo", vec![Component::from("foo")]),
("foo.", vec![Component::from("foo")]),
(".foo.", vec![Component::from("foo")]),
(
".foo.bar",
vec![Component::from("foo"), Component::from("bar")],
),
(
".foo.bar.",
vec![Component::from("foo"), Component::from("bar")],
),
(
"foo.bar",
vec![Component::from("foo"), Component::from("bar")],
),
(
"foo.bar.",
vec![Component::from("foo"), Component::from("bar")],
),
(
"foo..bar",
vec![Component::from("foo"), Component::from("bar")],
),
(
"foo.1:3#abcdef",
vec![
Component::from("foo"),
Component::from("a"),
Component::from("b"),
Component::from("c"),
Component::from("abcdef"),
],
),
(
"foo.2:3#zzzzzzzzzz",
vec![
Component::from("foo"),
Component::from("zz"),
Component::from("zz"),
Component::from("zz"),
Component::from("zzzzzzzzzz"),
],
),
(
"foo.1:3#abcdef.bar",
vec![
Component::from("foo"),
Component::from("a"),
Component::from("b"),
Component::from("c"),
Component::from("abcdef"),
Component::from("bar"),
],
),
] {
assert_eq!(Path::from(input), Path::from(output),);
}
}