use std::error::Error; use std::fmt; use std::future::Future; use std::pin::Pin; use std::sync::{Arc, RwLock}; use nostr_connect::client::AuthUrlHandler; use nostr_sdk::prelude::*; #[derive(Debug)] pub struct UniversalSignerError(Box); impl fmt::Display for UniversalSignerError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0) } } impl Error for UniversalSignerError { fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&*self.0) } } impl UniversalSignerError { pub fn new(err: E) -> Self where E: Error + Send + Sync + 'static, { UniversalSignerError(Box::new(err)) } } /// A type-erased signer whose inner signer can be swapped in-place /// (e.g. after login/logout). All clones see the swap. #[derive(Clone, Debug)] pub struct UniversalSigner { inner: Arc>>, } impl UniversalSigner { pub fn new(signer: T) -> Self where T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, { Self { inner: Arc::new(RwLock::new(Arc::new(InnerSignerImpl(signer)))), } } /// Swap the inner signer in-place. All clones see the new signer. pub fn swap_inner(&self, new_signer: T) where T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, { *self.inner.write().expect("RwLock poisoned") = Arc::new(InnerSignerImpl(new_signer)); } } trait InnerSigner: fmt::Debug + Send + Sync + 'static { fn get_public_key_async( &self, ) -> Pin> + Send + '_>>; fn sign_event_async( &self, unsigned: UnsignedEvent, ) -> Pin> + Send + '_>>; fn nip44_encrypt_async<'a>( &'a self, public_key: &'a PublicKey, content: &'a str, ) -> Pin> + Send + 'a>>; fn nip44_decrypt_async<'a>( &'a self, public_key: &'a PublicKey, payload: &'a str, ) -> Pin> + Send + 'a>>; } #[derive(Debug)] struct InnerSignerImpl(T); impl InnerSigner for InnerSignerImpl where T: AsyncGetPublicKey + AsyncSignEvent + AsyncNip44 + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, ::Error: Error + Send + Sync + 'static, { fn get_public_key_async( &self, ) -> Pin> + Send + '_>> { Box::pin(async move { AsyncGetPublicKey::get_public_key_async(&self.0) .await .map_err(UniversalSignerError::new) }) } fn sign_event_async( &self, unsigned: UnsignedEvent, ) -> Pin> + Send + '_>> { Box::pin(async move { AsyncSignEvent::sign_event_async(&self.0, unsigned) .await .map_err(UniversalSignerError::new) }) } fn nip44_encrypt_async<'a>( &'a self, public_key: &'a PublicKey, content: &'a str, ) -> Pin> + Send + 'a>> { Box::pin(async move { AsyncNip44::nip44_encrypt_async(&self.0, public_key, content) .await .map_err(UniversalSignerError::new) }) } fn nip44_decrypt_async<'a>( &'a self, public_key: &'a PublicKey, payload: &'a str, ) -> Pin> + Send + 'a>> { Box::pin(async move { AsyncNip44::nip44_decrypt_async(&self.0, public_key, payload) .await .map_err(UniversalSignerError::new) }) } } impl AsyncGetPublicKey for UniversalSigner { type Error = UniversalSignerError; fn get_public_key_async( &self, ) -> Pin> + Send + '_>> { let inner = self.inner.read().expect("RwLock poisoned").clone(); Box::pin(async move { inner.get_public_key_async().await }) } } impl AsyncSignEvent for UniversalSigner { type Error = UniversalSignerError; fn sign_event_async( &self, unsigned: UnsignedEvent, ) -> Pin> + Send + '_>> { let inner = self.inner.read().expect("RwLock poisoned").clone(); Box::pin(async move { inner.sign_event_async(unsigned).await }) } } impl AsyncNip44 for UniversalSigner { type Error = UniversalSignerError; fn nip44_encrypt_async<'a>( &'a self, public_key: &'a PublicKey, content: &'a str, ) -> Pin> + Send + 'a>> { let inner = self.inner.read().expect("RwLock poisoned").clone(); Box::pin(async move { inner.nip44_encrypt_async(public_key, content).await }) } fn nip44_decrypt_async<'a>( &'a self, public_key: &'a PublicKey, payload: &'a str, ) -> Pin> + Send + 'a>> { let inner = self.inner.read().expect("RwLock poisoned").clone(); Box::pin(async move { inner.nip44_decrypt_async(public_key, payload).await }) } } /// Opens the NIP-46 auth URL in the default browser. #[derive(Debug, Clone)] pub struct SignedAuthUrlHandler; impl AuthUrlHandler for SignedAuthUrlHandler { fn on_auth_url( &self, auth_url: Url, ) -> Pin> + Send + '_>> { Box::pin(async move { webbrowser::open(auth_url.as_str()).map_err(nostr_connect::error::Error::other)?; Ok(()) }) } }