//! Client to connet to mint use async_trait::async_trait; use cashu::error::ErrorResponse; #[cfg(feature = "nut07")] use cashu::nuts::CheckStateResponse; use cashu::nuts::{ BlindedMessage, CurrencyUnit, Id, KeySet, KeysetResponse, MeltBolt11Response, MeltQuoteBolt11Response, MintBolt11Response, MintInfo, MintQuoteBolt11Response, PreMintSecrets, Proof, SwapRequest, SwapResponse, }; #[cfg(feature = "nut07")] use cashu::secret::Secret; use cashu::Amount; use thiserror::Error; use url::Url; #[cfg(feature = "gloo")] pub mod gloo_client; #[cfg(not(target_arch = "wasm32"))] pub mod minreq_client; pub use cashu::Bolt11Invoice; #[derive(Debug, Error)] pub enum Error { #[error("Invoice not paid")] InvoiceNotPaid, #[error("Wallet not responding")] LightingWalletNotResponding(Option), /// Parse Url Error #[error("`{0}`")] UrlParse(#[from] url::ParseError), /// Serde Json error #[error("`{0}`")] SerdeJson(#[from] serde_json::Error), /// Cashu Url Error #[error("`{0}`")] CashuUrl(#[from] cashu::url::Error), /// Min req error #[cfg(not(target_arch = "wasm32"))] #[error("`{0}`")] MinReq(#[from] minreq::Error), #[cfg(feature = "gloo")] #[error("`{0}`")] Gloo(String), #[error("Unknown Error response")] UnknownErrorResponse(cashu::error::ErrorResponse), /// Custom Error #[error("`{0}`")] Custom(String), } impl From for Error { fn from(err: ErrorResponse) -> Error { Self::UnknownErrorResponse(err) } } #[async_trait(?Send)] pub trait Client { async fn get_mint_keys(&self, mint_url: Url) -> Result, Error>; async fn get_mint_keysets(&self, mint_url: Url) -> Result; async fn get_mint_keyset(&self, mint_url: Url, keyset_id: Id) -> Result; async fn post_mint_quote( &self, mint_url: Url, amount: Amount, unit: CurrencyUnit, ) -> Result; async fn post_mint( &self, mint_url: Url, quote: &str, premint_secrets: PreMintSecrets, ) -> Result; async fn post_melt_quote( &self, mint_url: Url, unit: CurrencyUnit, request: Bolt11Invoice, ) -> Result; async fn post_melt( &self, mint_url: Url, quote: String, inputs: Vec, outputs: Option>, ) -> Result; // REVIEW: Should be consistent aboue passing in the Request struct or the // compnatants and making it within the function. Here the struct is passed // in but in check spendable and melt the compants are passed in async fn post_swap( &self, mint_url: Url, split_request: SwapRequest, ) -> Result; #[cfg(feature = "nut07")] async fn post_check_state( &self, mint_url: Url, secrets: Vec, ) -> Result; async fn get_mint_info(&self, mint_url: Url) -> Result; } #[cfg(any(not(target_arch = "wasm32"), feature = "gloo"))] fn join_url(url: Url, paths: &[&str]) -> Result { let mut url = url; for path in paths { if !url.path().ends_with('/') { url.path_segments_mut() .map_err(|_| Error::Custom("Url Path Segmants".to_string()))? .push(path); } else { url.path_segments_mut() .map_err(|_| Error::Custom("Url Path Segmants".to_string()))? .pop() .push(path); } } Ok(url) } #[cfg(test)] mod tests { /* use super::*; #[test] fn test_decode_error() { let err = r#"{"code":0,"error":"Lightning invoice not paid yet."}"#; let error = Error::from_json(err).unwrap(); match error { Error::InvoiceNotPaid => {} _ => panic!("Wrong error"), } let err = r#"{"code": 0, "error": "Lightning wallet not responding: Failed to connect to https://legend.lnbits.com due to: All connection attempts failed"}"#; let error = Error::from_json(err).unwrap(); match error { Error::LightingWalletNotResponding(mint) => { assert_eq!(mint, Some("https://legend.lnbits.com".to_string())); } _ => panic!("Wrong error"), } } */ }