use crate::hash_path::path::Component;
use crate::hash_path::path::Path;
use crate::prelude::*;
use holochain_wasmer_guest::*;
pub const ROOT: &[u8; 2] = &[0x00, 0x00];
#[derive(PartialEq, SerializedBytes, serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct Anchor {
pub anchor_type: String,
pub anchor_text: Option<String>,
}
entry_def!(Anchor PathEntry::entry_def());
impl From<&Anchor> for Path {
fn from(anchor: &Anchor) -> Self {
let mut components = vec![
Component::new(ROOT.to_vec()),
Component::from(anchor.anchor_type.as_bytes().to_vec()),
];
if let Some(text) = anchor.anchor_text.as_ref() {
components.push(Component::from(text.as_bytes().to_vec()));
}
components.into()
}
}
impl TryFrom<&Path> for Anchor {
type Error = WasmError;
fn try_from(path: &Path) -> Result<Self, Self::Error> {
let components: Vec<Component> = path.as_ref().to_owned();
if components.len() == 2 || components.len() == 3 {
if components[0] == Component::new(ROOT.to_vec()) {
Ok(Anchor {
anchor_type: std::str::from_utf8(components[1].as_ref())
.map_err(|e| SerializedBytesError::Deserialize(e.to_string()))?
.to_string(),
anchor_text: {
match components.get(2) {
Some(component) => Some(
std::str::from_utf8(component.as_ref())
.map_err(|e| SerializedBytesError::Deserialize(e.to_string()))?
.to_string(),
),
None => None,
}
},
})
} else {
Err(WasmError::Serialize(SerializedBytesError::Deserialize(
format!(
"Bad anchor path root {:0?} should be {:1?}",
components[0].as_ref(),
ROOT,
),
)))
}
} else {
Err(WasmError::Serialize(SerializedBytesError::Deserialize(
format!("Bad anchor path length {}", components.len()),
)))
}
}
}
pub fn anchor(anchor_type: String, anchor_text: String) -> ExternResult<holo_hash::EntryHash> {
let path: Path = (&Anchor {
anchor_type,
anchor_text: Some(anchor_text),
})
.into();
path.ensure()?;
path.path_entry_hash()
}
pub fn list_anchor_type_addresses() -> ExternResult<Vec<EntryHash>> {
let links = Path::from(vec![Component::new(ROOT.to_vec())])
.children()?
.into_iter()
.map(|link| link.target)
.collect();
Ok(links)
}
pub fn list_anchor_addresses(anchor_type: String) -> ExternResult<Vec<EntryHash>> {
let path: Path = (&Anchor {
anchor_type,
anchor_text: None,
})
.into();
let links = path
.children()?
.into_iter()
.map(|link| link.target)
.collect();
Ok(links)
}
pub fn list_anchor_tags(anchor_type: String) -> ExternResult<Vec<String>> {
let path: Path = (&Anchor {
anchor_type,
anchor_text: None,
})
.into();
path.ensure()?;
let hopefully_anchor_tags: Result<Vec<String>, WasmError> = path
.children_paths()?
.into_iter()
.map(|path| match Anchor::try_from(&path) {
Ok(anchor) => match anchor.anchor_text {
Some(text) => Ok(text),
None => Err(WasmError::Serialize(SerializedBytesError::Deserialize(
"missing anchor text".into(),
))),
},
Err(e) => Err(e),
})
.collect();
let mut anchor_tags = hopefully_anchor_tags?;
anchor_tags.sort();
anchor_tags.dedup();
Ok(anchor_tags)
}
#[cfg(test)]
#[test]
fn hash_path_root() {
assert_eq!(ROOT, &[0_u8, 0]);
}
#[cfg(test)]
#[test]
fn hash_path_anchor_path() {
let examples = [
(
"foo",
None,
Path::from(vec![
Component::from(vec![0, 0]),
Component::from(vec![102, 111, 111]),
]),
),
(
"foo",
Some("bar".to_string()),
Path::from(vec![
Component::from(vec![0, 0]),
Component::from(vec![102, 111, 111]),
Component::from(vec![98, 97, 114]),
]),
),
];
for (atype, text, path) in examples {
assert_eq!(
path,
(&Anchor {
anchor_type: atype.to_string(),
anchor_text: text,
})
.into(),
);
}
}
#[cfg(test)]
#[test]
fn hash_path_anchor_entry_def() {
assert_eq!(PathEntry::entry_def_id(), Anchor::entry_def_id(),);
assert_eq!(PathEntry::crdt_type(), Anchor::crdt_type(),);
assert_eq!(
PathEntry::required_validations(),
Anchor::required_validations(),
);
assert_eq!(PathEntry::entry_visibility(), Anchor::entry_visibility(),);
assert_eq!(PathEntry::entry_def(), Anchor::entry_def(),);
}
#[cfg(test)]
#[test]
fn hash_path_anchor_from_path() {
let path = Path::from(vec![
Component::from(vec![0, 0]),
Component::from(vec![102, 111, 111]),
Component::from(vec![98, 97, 114]),
]);
assert_eq!(
Anchor::try_from(&path).unwrap(),
Anchor {
anchor_type: "foo".into(),
anchor_text: Some("bar".into()),
},
);
}