use std::{cell::RefCell, pin::Pin, rc::Weak};
use audi::Listener;
use credo::{Credential, CredentialSource, ScopeID};
use futures::{Future, FutureExt, StreamExt};
use litl::Litl;
use crate::{conventions::CREDENTIALS_DOC_NAME, Jazz, JazzInner};
pub struct AccountCredentialSource {
account_id: ScopeID,
account_credential: Credential,
jazz: Weak<RefCell<JazzInner>>,
}
impl AccountCredentialSource {
pub(crate) fn new(
account_id: ScopeID,
account_credential: Credential,
jazz_weak_ref: Weak<RefCell<JazzInner>>,
) -> Self {
Self {
account_id,
account_credential,
jazz: jazz_weak_ref,
}
}
}
impl CredentialSource for AccountCredentialSource {
fn add_credential<'a>(
&'a mut self,
scope_id: ScopeID,
credential: Credential,
) -> std::pin::Pin<Box<dyn futures::Future<Output = ()> + 'a>> {
if scope_id == self.account_id {
panic!("Can't add credential for account document");
}
let jazz = Jazz(self.jazz.upgrade().unwrap());
async move {
let account_doc_id = jazz
.resolve_named_document(self.account_id, CREDENTIALS_DOC_NAME)
.await;
let account_doc = jazz.load_document(account_doc_id).await;
let account_doc_view = account_doc.start_writing();
account_doc_view
.get_root()
.if_map_mut()
.expect("Expected account doc to be a map")
.insert(
Litl::from_se(&scope_id).tagged_data_to_raw_str().as_str(),
Litl::from_se(&credential),
);
account_doc.finish_writing(account_doc_view);
}
.boxed_local()
}
fn credentials_for<'a>(
&'a self,
scope_id: &'a ScopeID,
) -> Pin<Box<dyn Future<Output = Vec<Credential>> + 'a>> {
let jazz = Jazz(self.jazz.upgrade().unwrap());
async move {
if scope_id == &self.account_id {
vec![self.account_credential.clone()]
} else {
let account_doc_id = jazz
.resolve_named_document(self.account_id, CREDENTIALS_DOC_NAME)
.await;
if scope_id == &account_doc_id.0 {
vec![]
} else {
let account_doc = jazz.load_document(account_doc_id).await;
let (doc_updates_tx, mut doc_updates_rx) = futures::channel::mpsc::channel(10);
account_doc
.add_listener(Listener::new(
&format!(
"credentials_for_{}_{:?}",
Litl::from_se(scope_id).tagged_data_to_raw_str(),
ti64::now()
),
doc_updates_tx,
))
.await;
while let Some((root, _)) = doc_updates_rx.next().await {
if let Some(credential) = root.if_map().ok().and_then(|root_map| {
root_map
.get(Litl::from_se(&scope_id).tagged_data_to_raw_str().as_str())
.if_plain()
.ok()
.and_then(|credential_litl| {
credential_litl.clone().try_into_de().ok()
})
}) {
return vec![credential];
}
}
unreachable!("Account doc listener closed before credential was found");
}
}
}
.boxed_local()
}
}