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 holochain_zome_types::validate::RequiredValidationType;
use std::str::FromStr;
pub const DELIMITER: &str = ".";
#[derive(
Clone, PartialEq, Debug, Default, serde::Deserialize, serde::Serialize, SerializedBytes,
)]
#[repr(transparent)]
pub struct Component(#[serde(with = "serde_bytes")] Vec<u8>);
impl Component {
pub fn new(v: Vec<u8>) -> Self {
Self(v)
}
}
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: "hdk.path".into(),
required_validations: RequiredValidations::default(),
visibility: EntryVisibility::Public,
required_validation_type: RequiredValidationType::default(),
});
#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize, SerializedBytes)]
pub struct PathEntry(#[serde(with = "serde_bytes")] Vec<u8>);
impl PathEntry {
pub fn new(entry_hash: EntryHash) -> Self {
Self(entry_hash.get_raw_32().to_vec())
}
}
entry_def!(PathEntry EntryDef {
id: "hdk.path_entry".into(),
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 Path {
pub fn path_entry(&self) -> ExternResult<PathEntry> {
Ok(PathEntry::new(hash_entry(self)?))
}
pub fn path_entry_hash(&self) -> ExternResult<holo_hash::EntryHash> {
hash_entry(self.path_entry()?)
}
pub fn exists(&self) -> ExternResult<bool> {
Ok(get(self.path_entry_hash()?, GetOptions::content())?.is_some())
}
pub fn ensure(&self) -> ExternResult<()> {
if !self.exists()? {
create_entry(self.path_entry()?)?;
if let Some(parent) = self.parent() {
parent.ensure()?;
create_link(
parent.path_entry_hash()?.into(),
self.path_entry_hash()?.into(),
HdkLinkType::Paths,
LinkTag::new(match self.leaf() {
None => <Vec<u8>>::with_capacity(0),
Some(component) => {
UnsafeBytes::from(SerializedBytes::try_from(component)?).into()
}
}),
)?;
}
}
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<Vec<holochain_zome_types::link::Link>> {
Self::ensure(self)?;
let mut unwrapped = get_links(self.path_entry_hash()?.into(), None)?;
unwrapped.sort_unstable_by(|a, b| a.tag.cmp(&b.tag));
unwrapped.dedup_by(|a, b| a.tag.eq(&b.tag));
Ok(unwrapped)
}
pub fn children_paths(&self) -> ExternResult<Vec<Self>> {
let children = self.children()?;
let components: ExternResult<Vec<Option<Component>>> = children
.into_iter()
.map(|link| {
let component_bytes = &link.tag.0[..];
if component_bytes.is_empty() {
Ok(None)
} else {
Ok(Some(
SerializedBytes::from(UnsafeBytes::from(component_bytes.to_vec()))
.try_into()
.map_err(WasmError::Serialize)?,
))
}
})
.collect();
Ok(components?
.into_iter()
.map(|maybe_component| {
let mut new_path = self.clone();
if let Some(component) = maybe_component {
new_path.append_component(component);
}
new_path
})
.collect())
}
pub fn children_details(&self) -> ExternResult<holochain_zome_types::link::LinkDetails> {
Self::ensure(self)?;
get_link_details(
self.path_entry_hash()?.into(),
Some(holochain_zome_types::link::LinkTag::new([])),
)
}
pub fn append_component(&mut self, component: Component) {
self.0.push(component);
}
pub fn leaf(&self) -> Option<&Component> {
self.0.last()
}
}
#[test]
#[cfg(test)]
fn hash_path_delimiter() {
assert_eq!(".", DELIMITER,);
}
#[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),);
}
}