commit 37d0f68ad69c884d2b133ff4bef49c2131119a4b
parent 0dfe1fbee00440940826e57ec80a89a266d938bb
Author: triesap <tyson@radroots.org>
Date: Sat, 28 Mar 2026 18:01:44 +0000
nostr-connect: add typed pending poll outcomes
Diffstat:
3 files changed, 82 insertions(+), 3 deletions(-)
diff --git a/crates/nostr-connect/src/lib.rs b/crates/nostr-connect/src/lib.rs
@@ -9,7 +9,8 @@ pub mod uri;
pub mod prelude {
pub use crate::error::RadrootsNostrConnectError;
pub use crate::message::{
- RADROOTS_NOSTR_CONNECT_RPC_KIND, RadrootsNostrConnectRequest,
+ RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RADROOTS_NOSTR_CONNECT_RPC_KIND,
+ RadrootsNostrConnectPendingConnectionPollOutcome, RadrootsNostrConnectRequest,
RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse,
RadrootsNostrConnectResponseEnvelope,
};
diff --git a/crates/nostr-connect/src/message.rs b/crates/nostr-connect/src/message.rs
@@ -241,6 +241,17 @@ pub struct RadrootsNostrConnectResponseEnvelope {
pub error: Option<String>,
}
+pub const RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR: &str = "connection is pending";
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum RadrootsNostrConnectPendingConnectionPollOutcome {
+ PendingApproval,
+ Approved(PublicKey),
+ Rejected { message: String },
+ AuthChallenge { url: String },
+ UnexpectedResponse { response: String },
+}
+
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RadrootsNostrConnectResponse {
ConnectAcknowledged,
@@ -266,6 +277,29 @@ pub enum RadrootsNostrConnectResponse {
}
impl RadrootsNostrConnectResponse {
+ pub fn into_pending_connection_poll_outcome(
+ self,
+ ) -> RadrootsNostrConnectPendingConnectionPollOutcome {
+ match self {
+ Self::UserPublicKey(public_key) => {
+ RadrootsNostrConnectPendingConnectionPollOutcome::Approved(public_key)
+ }
+ Self::Error { result, error } => {
+ if result.is_none() && error == RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR {
+ RadrootsNostrConnectPendingConnectionPollOutcome::PendingApproval
+ } else {
+ RadrootsNostrConnectPendingConnectionPollOutcome::Rejected { message: error }
+ }
+ }
+ Self::AuthUrl(url) => {
+ RadrootsNostrConnectPendingConnectionPollOutcome::AuthChallenge { url }
+ }
+ other => RadrootsNostrConnectPendingConnectionPollOutcome::UnexpectedResponse {
+ response: format!("{other:?}"),
+ },
+ }
+ }
+
pub fn into_envelope(
self,
id: impl Into<String>,
diff --git a/crates/nostr-connect/tests/coverage.rs b/crates/nostr-connect/tests/coverage.rs
@@ -1,7 +1,8 @@
use nostr::{Event, EventBuilder, Keys, PublicKey, RelayUrl, SecretKey, Timestamp, UnsignedEvent};
use radroots_nostr_connect::prelude::{
- RadrootsNostrConnectError, RadrootsNostrConnectMethod, RadrootsNostrConnectPermission,
- RadrootsNostrConnectPermissions, RadrootsNostrConnectRequest,
+ RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR, RadrootsNostrConnectError,
+ RadrootsNostrConnectMethod, RadrootsNostrConnectPendingConnectionPollOutcome,
+ RadrootsNostrConnectPermission, RadrootsNostrConnectPermissions, RadrootsNostrConnectRequest,
RadrootsNostrConnectRequestMessage, RadrootsNostrConnectResponse,
RadrootsNostrConnectResponseEnvelope, RadrootsNostrConnectUri,
};
@@ -1005,3 +1006,46 @@ fn response_surface_covers_success_and_error_paths() {
Err(RadrootsNostrConnectError::InvalidResponsePayload { .. })
));
}
+
+#[test]
+fn pending_connection_poll_outcome_uses_typed_variants() {
+ assert_eq!(
+ RadrootsNostrConnectResponse::Error {
+ result: None,
+ error: RADROOTS_NOSTR_CONNECT_PENDING_CONNECTION_ERROR.to_owned(),
+ }
+ .into_pending_connection_poll_outcome(),
+ RadrootsNostrConnectPendingConnectionPollOutcome::PendingApproval
+ );
+
+ assert_eq!(
+ RadrootsNostrConnectResponse::UserPublicKey(test_public_key())
+ .into_pending_connection_poll_outcome(),
+ RadrootsNostrConnectPendingConnectionPollOutcome::Approved(test_public_key())
+ );
+
+ assert_eq!(
+ RadrootsNostrConnectResponse::Error {
+ result: Some(json!("partial")),
+ error: "rejected".to_owned(),
+ }
+ .into_pending_connection_poll_outcome(),
+ RadrootsNostrConnectPendingConnectionPollOutcome::Rejected {
+ message: "rejected".to_owned(),
+ }
+ );
+
+ assert_eq!(
+ RadrootsNostrConnectResponse::AuthUrl("https://auth.example.com/challenge".to_owned())
+ .into_pending_connection_poll_outcome(),
+ RadrootsNostrConnectPendingConnectionPollOutcome::AuthChallenge {
+ url: "https://auth.example.com/challenge".to_owned(),
+ }
+ );
+
+ assert!(matches!(
+ RadrootsNostrConnectResponse::Pong.into_pending_connection_poll_outcome(),
+ RadrootsNostrConnectPendingConnectionPollOutcome::UnexpectedResponse { response }
+ if response == "Pong"
+ ));
+}