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>,
}
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| wasm_error!(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| {
wasm_error!(SerializedBytesError::Deserialize(
e.to_string()
))
})?
.to_string(),
),
None => None,
}
},
})
} else {
Err(wasm_error!(WasmErrorInner::Serialize(
SerializedBytesError::Deserialize(format!(
"Bad anchor path root {:0?} should be {:1?}",
components[0].as_ref(),
ROOT,
),)
)))
}
} else {
Err(wasm_error!(WasmErrorInner::Serialize(
SerializedBytesError::Deserialize(format!(
"Bad anchor path length {}",
components.len()
),)
)))
}
}
}
pub fn anchor<T, E>(
link_type: T,
anchor_type: String,
anchor_text: String,
) -> ExternResult<holo_hash::EntryHash>
where
ScopedLinkType: TryFrom<T, Error = E>,
WasmError: From<E>,
{
let path: Path = (&Anchor {
anchor_type,
anchor_text: Some(anchor_text),
})
.into();
let path = path.typed(link_type)?;
path.ensure()?;
path.path_entry_hash()
}
pub fn list_anchor_type_addresses<T, E>(link_type: T) -> ExternResult<Vec<AnyLinkableHash>>
where
ScopedLinkType: TryFrom<T, Error = E>,
WasmError: From<E>,
{
let links = Path::from(vec![Component::new(ROOT.to_vec())])
.typed(link_type)?
.children()?
.into_iter()
.map(|link| link.target)
.collect();
Ok(links)
}
pub fn list_anchor_addresses<T, E>(
link_type: T,
anchor_type: String,
) -> ExternResult<Vec<AnyLinkableHash>>
where
ScopedLinkType: TryFrom<T, Error = E>,
WasmError: From<E>,
{
let path: Path = (&Anchor {
anchor_type,
anchor_text: None,
})
.into();
let links = path
.typed(link_type)?
.children()?
.into_iter()
.map(|link| link.target)
.collect();
Ok(links)
}
pub fn list_anchor_tags<T, E>(link_type: T, anchor_type: String) -> ExternResult<Vec<String>>
where
ScopedLinkType: TryFrom<T, Error = E>,
WasmError: From<E>,
{
let path: Path = (&Anchor {
anchor_type,
anchor_text: None,
})
.into();
let path = path.typed(link_type)?;
path.ensure()?;
let hopefully_anchor_tags: Result<Vec<String>, WasmError> = path
.children_paths()?
.into_iter()
.map(|path| match Anchor::try_from(&path.path) {
Ok(anchor) => match anchor.anchor_text {
Some(text) => Ok(text),
None => Err(wasm_error!(WasmErrorInner::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_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()),
},
);
}