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::error::Error as SignerError; use nostr_sdk::prelude::*; /// A type-erased signer whose inner signer can be swapped in-place. #[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(SignerError::other) }) } fn sign_event_async( &self, unsigned: UnsignedEvent, ) -> Pin> + Send + '_>> { Box::pin(async move { AsyncSignEvent::sign_event_async(&self.0, unsigned) .await .map_err(SignerError::other) }) } 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(SignerError::other) }) } 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(SignerError::other) }) } } impl AsyncGetPublicKey for UniversalSigner { type Error = SignerError; 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 = SignerError; 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 = SignerError; 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(()) }) } }