use hdk::prelude::*;
use hc_zome_profiles_integrity::*;
use crate::utils::*;
#[hdk_extern]
pub fn create_profile(pair: (Profile, AgentPubKey)) -> ExternResult<Record> {
let profile = pair.0;
let agent_address = pair.1;
let ah = create_entry(EntryTypes::Profile(profile.clone()))?;
let path = prefix_path(profile.nickname.clone())?;
path.ensure()?;
create_link(
path.path_entry_hash()?,
agent_address.clone(),
LinkTypes::PathToAgent,
LinkTag::new(profile.nickname.to_lowercase().as_bytes().to_vec()),
)?;
create_link(
agent_address,
ah.clone(),
LinkTypes::AgentToProfile,
(),
)?;
let record = get(ah, GetOptions::default())?
.ok_or(wasm_error!(WasmErrorInner::Guest("Unreachable".into())))?;
Ok(record)
}
#[hdk_extern]
pub fn update_profile(pair: (Profile, AgentPubKey)) -> ExternResult<Record> {
let profile = pair.0;
let agent_address = pair.1;
let previous_profile_record = get_profile(agent_address.clone())?
.ok_or(wasm_error!(WasmErrorInner::Guest(
"I haven't created a profile yet".into(),
)))?;
let action_hash = update_entry(previous_profile_record.action_address().clone(), &profile)?;
let previous_profile: Profile = previous_profile_record
.entry()
.to_app_option()
.map_err(|e| wasm_error!(e))?
.ok_or(wasm_error!(WasmErrorInner::Guest(
"Previous profile is malformed".to_string()
)))?;
if previous_profile.nickname.ne(&profile.nickname) {
let previous_prefix_path = prefix_path(previous_profile.nickname)?;
let links = get_links(
previous_prefix_path.path_entry_hash()?,
LinkTypes::PathToAgent,
None,
)?;
for l in links {
if let Ok(pub_key) = AgentPubKey::try_from(l.target) {
if agent_address.eq(&pub_key) {
delete_link(l.create_link_hash)?;
}
}
}
let path = prefix_path(profile.nickname.clone())?;
path.ensure()?;
create_link(
path.path_entry_hash()?,
agent_address,
LinkTypes::PathToAgent,
LinkTag::new(profile.nickname.to_lowercase().as_bytes().to_vec()),
)?;
}
let record = get(action_hash, GetOptions::default())?
.ok_or(wasm_error!(WasmErrorInner::Guest("Unreachable".into())))?;
Ok(record)
}
#[hdk_extern]
pub fn search_agents(nickname_filter: String) -> ExternResult<Vec<AgentPubKey>> {
if nickname_filter.len() < 3 {
return Err(wasm_error!(WasmErrorInner::Guest(
"Cannot search with a prefix less than 3 characters".into(),
)));
}
let prefix_path = prefix_path(nickname_filter.clone())?;
let links = get_links(
prefix_path.path_entry_hash()?,
LinkTypes::PathToAgent,
Some(LinkTag::new(
nickname_filter.to_lowercase().as_bytes().to_vec(),
)),
)?;
let mut agents: Vec<AgentPubKey> = vec![];
for link in links {
if let Ok(pub_key) = AgentPubKey::try_from(link.target) {
agents.push(pub_key);
}
}
Ok(agents)
}
#[hdk_extern]
pub fn get_profile(agent_pub_key: AgentPubKey) -> ExternResult<Option<Record>> {
let links = get_links(agent_pub_key, LinkTypes::AgentToProfile, None)?;
if links.len() == 0 {
return Ok(None);
}
let link = links[0].clone();
let profile = get_latest(link.target.into_action_hash().ok_or(wasm_error!(
WasmErrorInner::Guest("Profile link target is not of ActionHash".into())
))?)?;
Ok(Some(profile))
}
fn get_latest(action_hash: ActionHash) -> ExternResult<Record> {
let details = get_details(action_hash, GetOptions::default())?.ok_or(wasm_error!(
WasmErrorInner::Guest("Profile not found".into())
))?;
match details {
Details::Entry(_) => Err(wasm_error!(WasmErrorInner::Guest(
"Malformed details".into()
))),
Details::Record(element_details) => match element_details.updates.last() {
Some(update) => get_latest(update.action_address().clone()),
None => Ok(element_details.record),
},
}
}
#[hdk_extern]
pub fn get_agents_with_profile(_: ()) -> ExternResult<Vec<AgentPubKey>> {
let path = Path::from("all_profiles").typed(LinkTypes::PrefixPath)?;
let children = path.children_paths()?;
let get_links_input: Vec<GetLinksInput> = children
.into_iter()
.map(|path| {
Ok(GetLinksInput::new(
path.path_entry_hash()?.into(),
LinkTypes::PathToAgent.try_into_filter()?,
None,
))
})
.collect::<ExternResult<Vec<GetLinksInput>>>()?;
let links = HDK
.with(|h| h.borrow().get_links(get_links_input))?
.into_iter()
.flatten()
.collect::<Vec<Link>>();
let mut agents: Vec<AgentPubKey> = vec![];
for link in links {
if let Ok(pub_key) = AgentPubKey::try_from(link.target) {
agents.push(pub_key);
}
}
Ok(agents)
}