diff --git a/crates/cdk/src/nuts/nut17.rs b/crates/cdk/src/nuts/nut17/mod.rs similarity index 71% rename from crates/cdk/src/nuts/nut17.rs rename to crates/cdk/src/nuts/nut17/mod.rs index 5af295aa..f10e46f5 100644 --- a/crates/cdk/src/nuts/nut17.rs +++ b/crates/cdk/src/nuts/nut17/mod.rs @@ -1,15 +1,32 @@ //! Specific Subscription for the cdk crate +use super::{BlindSignature, CurrencyUnit, PaymentMethod}; +#[cfg(feature = "mint")] +use crate::cdk_database::{self, MintDatabase}; +pub use crate::pub_sub::SubId; use crate::{ - cdk_database::{self, MintDatabase}, nuts::{ MeltQuoteBolt11Response, MeltQuoteState, MintQuoteBolt11Response, MintQuoteState, ProofState, }, - pub_sub::{self, Index, Indexable, OnNewSubscription, SubscriptionGlobalId}, + pub_sub::{self, Index, Indexable, SubscriptionGlobalId}, }; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, ops::Deref, sync::Arc}; +use std::ops::Deref; +#[cfg(feature = "mint")] +use std::sync::Arc; + +#[cfg(feature = "mint")] +mod on_subscription; + +#[cfg(feature = "mint")] +pub use on_subscription::OnSubscription; + +#[cfg(not(feature = "mint"))] +mod on_subscription_no_op; + +#[cfg(not(feature = "mint"))] +pub use on_subscription_no_op::OnSubscription; /// Subscription Parameter according to the standard #[derive(Debug, Clone, Serialize, Deserialize)] @@ -58,10 +75,6 @@ impl Default for SupportedMethods { } } -pub use crate::pub_sub::SubId; - -use super::{BlindSignature, CurrencyUnit, PaymentMethod, PublicKey}; - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] #[serde(untagged)] /// Subscription response @@ -145,125 +158,28 @@ impl From for Vec> { } } -#[derive(Default)] -/// Subscription Init -/// -/// This struct triggers code when a new subscription is created. -/// -/// It is used to send the initial state of the subscription to the client. -pub struct SubscriptionInit(Option + Send + Sync>>); - -#[async_trait::async_trait] -impl OnNewSubscription for SubscriptionInit { - type Event = NotificationPayload; - type Index = (String, Kind); - - async fn on_new_subscription( - &self, - request: &[&Self::Index], - ) -> Result, String> { - let datastore = if let Some(localstore) = self.0.as_ref() { - localstore - } else { - return Ok(vec![]); - }; - - let mut to_return = vec![]; - - for (kind, values) in request.iter().fold( - HashMap::new(), - |mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| { - acc.entry(kind).or_default().push(data); - acc - }, - ) { - match kind { - Kind::Bolt11MeltQuote => { - let queries = values - .iter() - .map(|id| datastore.get_melt_quote(id)) - .collect::>(); - - to_return.extend( - futures::future::try_join_all(queries) - .await - .map(|quotes| { - quotes - .into_iter() - .filter_map(|quote| quote.map(|x| x.into())) - .map(|x: MeltQuoteBolt11Response| x.into()) - .collect::>() - }) - .map_err(|e| e.to_string())?, - ); - } - Kind::Bolt11MintQuote => { - let queries = values - .iter() - .map(|id| datastore.get_mint_quote(id)) - .collect::>(); - - to_return.extend( - futures::future::try_join_all(queries) - .await - .map(|quotes| { - quotes - .into_iter() - .filter_map(|quote| quote.map(|x| x.into())) - .map(|x: MintQuoteBolt11Response| x.into()) - .collect::>() - }) - .map_err(|e| e.to_string())?, - ); - } - Kind::ProofState => { - let public_keys = values - .iter() - .map(PublicKey::from_hex) - .collect::, _>>() - .map_err(|e| e.to_string())?; - - to_return.extend( - datastore - .get_proofs_states(&public_keys) - .await - .map_err(|e| e.to_string())? - .into_iter() - .enumerate() - .filter_map(|(idx, state)| { - state.map(|state| (public_keys[idx], state).into()) - }) - .map(|state: ProofState| state.into()), - ); - } - } - } - - Ok(to_return) - } -} - /// Manager /// Publish–subscribe manager /// /// Nut-17 implementation is system-wide and not only through the WebSocket, so /// it is possible for another part of the system to subscribe to events. -pub struct PubSubManager(pub_sub::Manager); +pub struct PubSubManager(pub_sub::Manager); impl Default for PubSubManager { fn default() -> Self { - PubSubManager(SubscriptionInit::default().into()) + PubSubManager(OnSubscription::default().into()) } } +#[cfg(feature = "mint")] impl From + Send + Sync>> for PubSubManager { fn from(val: Arc + Send + Sync>) -> Self { - PubSubManager(SubscriptionInit(Some(val)).into()) + PubSubManager(OnSubscription(Some(val)).into()) } } impl Deref for PubSubManager { - type Target = pub_sub::Manager; + type Target = pub_sub::Manager; fn deref(&self) -> &Self::Target { &self.0 diff --git a/crates/cdk/src/nuts/nut17/on_subscription.rs b/crates/cdk/src/nuts/nut17/on_subscription.rs new file mode 100644 index 00000000..b2347e60 --- /dev/null +++ b/crates/cdk/src/nuts/nut17/on_subscription.rs @@ -0,0 +1,110 @@ +//! On Subscription +//! +//! This module contains the code that is triggered when a new subscription is created. +use super::{Kind, NotificationPayload}; +use crate::{ + cdk_database::{self, MintDatabase}, + nuts::{MeltQuoteBolt11Response, MintQuoteBolt11Response, ProofState, PublicKey}, + pub_sub::OnNewSubscription, +}; +use std::{collections::HashMap, sync::Arc}; + +#[derive(Default)] +/// Subscription Init +/// +/// This struct triggers code when a new subscription is created. +/// +/// It is used to send the initial state of the subscription to the client. +pub struct OnSubscription( + pub(crate) Option + Send + Sync>>, +); + +#[async_trait::async_trait] +impl OnNewSubscription for OnSubscription { + type Event = NotificationPayload; + type Index = (String, Kind); + + async fn on_new_subscription( + &self, + request: &[&Self::Index], + ) -> Result, String> { + let datastore = if let Some(localstore) = self.0.as_ref() { + localstore + } else { + return Ok(vec![]); + }; + + let mut to_return = vec![]; + + for (kind, values) in request.iter().fold( + HashMap::new(), + |mut acc: HashMap<&Kind, Vec<&String>>, (data, kind)| { + acc.entry(kind).or_default().push(data); + acc + }, + ) { + match kind { + Kind::Bolt11MeltQuote => { + let queries = values + .iter() + .map(|id| datastore.get_melt_quote(id)) + .collect::>(); + + to_return.extend( + futures::future::try_join_all(queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MeltQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + } + Kind::Bolt11MintQuote => { + let queries = values + .iter() + .map(|id| datastore.get_mint_quote(id)) + .collect::>(); + + to_return.extend( + futures::future::try_join_all(queries) + .await + .map(|quotes| { + quotes + .into_iter() + .filter_map(|quote| quote.map(|x| x.into())) + .map(|x: MintQuoteBolt11Response| x.into()) + .collect::>() + }) + .map_err(|e| e.to_string())?, + ); + } + Kind::ProofState => { + let public_keys = values + .iter() + .map(PublicKey::from_hex) + .collect::, _>>() + .map_err(|e| e.to_string())?; + + to_return.extend( + datastore + .get_proofs_states(&public_keys) + .await + .map_err(|e| e.to_string())? + .into_iter() + .enumerate() + .filter_map(|(idx, state)| { + state.map(|state| (public_keys[idx], state).into()) + }) + .map(|state: ProofState| state.into()), + ); + } + } + } + + Ok(to_return) + } +} diff --git a/crates/cdk/src/nuts/nut17/on_subscription_no_op.rs b/crates/cdk/src/nuts/nut17/on_subscription_no_op.rs new file mode 100644 index 00000000..9db4a1a8 --- /dev/null +++ b/crates/cdk/src/nuts/nut17/on_subscription_no_op.rs @@ -0,0 +1,28 @@ +//! On Subscription - No +//! +//! This module contains the code that is triggered when a new subscription is +//! created, which is a no-op, since the crate is not compiled with the `mint` +//! feature, therefore there is no local store to query. +use super::{Kind, NotificationPayload}; +use crate::pub_sub::OnNewSubscription; + +#[derive(Default)] +/// Subscription Init +/// +/// This struct triggers code when a new subscription is created. +/// +/// It is used to send the initial state of the subscription to the client. +pub struct OnSubscription; + +#[async_trait::async_trait] +impl OnNewSubscription for OnSubscription { + type Event = NotificationPayload; + type Index = (String, Kind); + + async fn on_new_subscription( + &self, + _request: &[&Self::Index], + ) -> Result, String> { + Ok(vec![]) + } +}