use super::{
PrivateDirectory, PrivateFile, PrivateNode, PrivateRef, forest::traits::PrivateForest,
};
use crate::utils::OnceCellDebug;
use anyhow::{Result, anyhow};
use async_once_cell::OnceCell;
use async_recursion::async_recursion;
use multihash::Multihash;
use rand_core::CryptoRngCore;
use wnfs_common::{
BlockStore, Cid,
utils::{Arc, CondSend},
};
use wnfs_nameaccumulator::Name;
pub(crate) enum PrivateLink {
Encrypted {
private_ref: PrivateRef,
cache: OnceCell<PrivateNode>,
},
Decrypted {
node: PrivateNode,
},
}
impl std::fmt::Debug for PrivateLink {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Encrypted { private_ref, cache } => f
.debug_struct("Encrypted")
.field("private_ref", private_ref)
.field("cache", &OnceCellDebug(cache.get()))
.finish(),
Self::Decrypted { node } => f.debug_struct("Decrypted").field("node", node).finish(),
}
}
}
impl PrivateLink {
pub(crate) fn from_ref(private_ref: PrivateRef) -> Self {
Self::Encrypted {
private_ref,
cache: OnceCell::new(),
}
}
#[cfg_attr(not(target_arch = "wasm32"), async_recursion)]
#[cfg_attr(target_arch = "wasm32", async_recursion(?Send))]
pub(crate) async fn resolve_ref(
&self,
forest: &mut impl PrivateForest,
store: &impl BlockStore,
rng: &mut (impl CryptoRngCore + CondSend),
) -> Result<PrivateRef> {
match self {
Self::Encrypted { private_ref, .. } => Ok(private_ref.clone()),
Self::Decrypted { node } => {
Ok(node.store_and_get_private_ref(forest, store, rng).await?)
}
}
}
pub(crate) async fn resolve_node(
&self,
forest: &impl PrivateForest,
store: &impl BlockStore,
parent_name: Option<Name>,
) -> Result<&PrivateNode> {
match self {
Self::Encrypted { private_ref, cache } => {
cache
.get_or_try_init(PrivateNode::from_private_ref(
private_ref,
forest,
store,
parent_name,
))
.await
}
Self::Decrypted { node, .. } => Ok(node),
}
}
pub(crate) async fn resolve_node_mut(
&mut self,
forest: &impl PrivateForest,
store: &impl BlockStore,
parent_name: Option<Name>,
) -> Result<&mut PrivateNode> {
match self {
Self::Encrypted { private_ref, cache } => {
let private_node = match cache.take() {
Some(node) => node,
None => {
PrivateNode::from_private_ref(private_ref, forest, store, parent_name)
.await?
}
};
*self = Self::Decrypted { node: private_node };
Ok(match self {
Self::Decrypted { node, .. } => node,
_ => unreachable!(),
})
}
Self::Decrypted { node, .. } => Ok(node),
}
}
pub(crate) async fn resolve_owned_node(
self,
forest: &impl PrivateForest,
store: &impl BlockStore,
parent_name: Option<Name>,
) -> Result<PrivateNode> {
match self {
Self::Encrypted { private_ref, cache } => match cache.into_inner() {
Some(cached) => Ok(cached),
None => {
let node =
PrivateNode::from_private_ref(&private_ref, forest, store, parent_name)
.await?;
node.get_persisted_as()
.get_or_init(async { private_ref.content_cid })
.await;
Ok(node)
}
},
Self::Decrypted { node, .. } => Ok(node),
}
}
pub fn get_content_cid(&self) -> Option<&Cid> {
match self {
Self::Encrypted { private_ref, .. } => Some(&private_ref.content_cid),
Self::Decrypted { node } => node.get_persisted_as().get(),
}
}
#[inline]
pub(crate) fn with_dir(dir: PrivateDirectory) -> Self {
Self::from(PrivateNode::Dir(Arc::new(dir)))
}
#[inline]
pub(crate) fn with_file(file: PrivateFile) -> Self {
Self::from(PrivateNode::File(Arc::new(file)))
}
pub(crate) fn crdt_tiebreaker(&self) -> Result<Multihash<64>> {
Ok(*self.get_content_cid().ok_or_else(|| anyhow!("Impossible case: CRDT tiebreaker needed on node wasn't persisted before tie breaking"))?.hash())
}
}
impl PartialEq for PrivateLink {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(
Self::Encrypted {
private_ref: l_private_ref,
..
},
Self::Encrypted {
private_ref: r_private_ref,
..
},
) => l_private_ref == r_private_ref,
(Self::Decrypted { node: l_node, .. }, Self::Decrypted { node: r_node, .. }) => {
l_node == r_node
}
(Self::Encrypted { private_ref, cache }, Self::Decrypted { node }) => {
Some(&private_ref.content_cid) == node.get_persisted_as().get()
|| Some(node) == cache.get()
}
(Self::Decrypted { node }, Self::Encrypted { private_ref, cache }) => {
Some(&private_ref.content_cid) == node.get_persisted_as().get()
|| Some(node) == cache.get()
}
}
}
}
impl Clone for PrivateLink {
fn clone(&self) -> Self {
match self {
Self::Encrypted { private_ref, cache } => Self::Encrypted {
private_ref: private_ref.clone(),
cache: cache
.get()
.cloned()
.map(OnceCell::new_with)
.unwrap_or_default(),
},
Self::Decrypted { node } => Self::Decrypted { node: node.clone() },
}
}
}
impl From<PrivateNode> for PrivateLink {
fn from(node: PrivateNode) -> Self {
Self::Decrypted { node }
}
}