use anyhow::Result;
use cid::Cid;
use libipld_cbor::DagCborCodec;
use noosphere_storage::BlockStore;
use serde::{Deserialize, Serialize};
use std::{convert::TryFrom, fmt::Display, ops::Deref};
use ucan::{store::UcanJwtStore, Ucan};
use super::{Did, IdentitiesIpld, Jwt, Link};
#[cfg(docs)]
use crate::data::SphereIpld;
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Hash)]
pub struct AddressBookIpld {
pub identities: Link<IdentitiesIpld>,
}
impl AddressBookIpld {
pub async fn empty<S: BlockStore>(store: &mut S) -> Result<Self> {
let identities_ipld = IdentitiesIpld::empty(store).await?;
let identities = store.save::<DagCborCodec, _>(identities_ipld).await?.into();
Ok(AddressBookIpld { identities })
}
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Hash)]
pub struct IdentityIpld {
pub did: Did,
pub link_record: Option<Link<LinkRecord>>,
}
impl IdentityIpld {
pub async fn link_record<S: UcanJwtStore>(&self, store: &S) -> Option<LinkRecord> {
match &self.link_record {
Some(cid) => store
.read_token(cid)
.await
.unwrap_or(None)
.map(|jwt| LinkRecord(jwt.into())),
_ => None,
}
}
}
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize, Hash)]
#[serde(from = "Jwt", into = "Jwt")]
#[repr(transparent)]
pub struct LinkRecord(Jwt);
impl LinkRecord {
pub async fn dereference(&self) -> Option<Cid> {
let token = &self.0;
let ucan = match Ucan::try_from(token.to_string()) {
Ok(ucan) => ucan,
_ => return None,
};
let facts = ucan.facts();
for fact in facts {
match fact.as_object() {
Some(fields) => match fields.get("link") {
Some(cid_string) => {
match Cid::try_from(cid_string.as_str().unwrap_or_default()) {
Ok(cid) => return Some(cid),
Err(error) => {
warn!(
"Could not parse '{}' as name record link: {}",
cid_string, error
);
continue;
}
}
}
None => {
warn!("No 'link' field in fact, skipping...");
continue;
}
},
None => {
warn!("Fact is not an object, skipping...");
continue;
}
}
}
warn!("No facts contained a link!");
None
}
}
impl Deref for LinkRecord {
type Target = Jwt;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Display for LinkRecord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl From<Jwt> for LinkRecord {
fn from(value: Jwt) -> Self {
LinkRecord(value)
}
}
impl Into<Jwt> for LinkRecord {
fn into(self) -> Jwt {
self.0
}
}