Skip to main content

lexe_payment_uri_core/
lib.rs

1//! Core types and logic required to permissively parse Bitcoin / lightning
2//! payment addresses and URIs. For actually *resolving* a [`PaymentUri`] into a
3//! [`PaymentMethod`], which frequently requires accessing a network, see the
4//! [`lexe-payment-uri`] crate.
5//!
6//! # Permissive parsing
7//!
8//! This crate parses various BTC-related payment methods permissively.
9//! That means we accept inputs that are not strictly well-formed.
10//!
11//! Other wallet parsers for comparison:
12//! + [ACINQ/phoenix - Parser](https://github.com/ACINQ/phoenix/blob/master/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/Parser.kt)
13//! + [breez/breez-sdk - input_parser.rs](https://github.com/breez/breez-sdk-greenlight/blob/main/libs/sdk-common/src/input_parser.rs)
14//! + [MutinyWallet/bitcoin_waila (unmaintained)](https://github.com/MutinyWallet/bitcoin-waila/blob/master/waila/src/lib.rs)
15//!
16//! # Prompts
17//!
18//! ```md
19//! Please read these URI parser impls:
20//! - `~/lexe/github/breez-sdk-greenlight/libs/sdk-common/src/input_parser.rs`
21//! - `~/lexe/github/phoenix/phoenix-shared/src/commonMain/kotlin/fr.acinq.phoenix/utils/Parser.kt` (and files nearby)
22//! - `~/lexe/github/bitcoin-waila/waila/src/lib.rs`
23//! ```
24//!
25//! [`PaymentUri`]: payment_uri::PaymentUri
26//! [`PaymentMethod`]: payment_method::PaymentMethod
27
28// `proptest_derive::Arbitrary` issue. This will hard-error for edition 2024 so
29// hopefully it gets fixed soon...
30// See: <https://github.com/proptest-rs/proptest/issues/447>
31#![allow(non_local_definitions)]
32
33use std::{
34    borrow::Cow,
35    fmt::{self, Display},
36};
37
38use lexe_api_core::types::{invoice, offer};
39
40/// Export all public types so they are accessible via the crate root.
41/// The containing modules are used only for internal organization, and are
42/// intentionally private so crate users have a simple, flat namespace.
43pub use crate::{
44    bip321_uri::Bip321Uri,
45    email_like::EmailLikeAddress,
46    lightning_uri::LightningUri,
47    lnurl::Lnurl,
48    payment_method::{OfferWithAmount, Onchain, PaymentMethod},
49    payment_uri::PaymentUri,
50};
51
52/// Helper functions and utilities; some of these are public.
53pub mod helpers;
54
55/// BIP321 / BIP21 parsing and formatting.
56mod bip321_uri;
57/// Email-like payment URIs, including Lightning Addresses and BIP353.
58mod email_like;
59/// "lightning:" URIs, containing a BOLT 11 invoice or BOLT12 offer.
60mod lightning_uri;
61/// LNURLs.
62mod lnurl;
63/// `PaymentMethod` and subtypes, representing a resolved payment method.
64mod payment_method;
65/// Top level `PaymentUri` representing a parsed payment URI or address.
66mod payment_uri;
67/// Low level URI building blocks: `Uri`, `UriParam`, `UriParamKey`
68mod uri;
69
70#[derive(Clone, Debug, PartialEq)]
71pub enum Error {
72    /// [`PaymentUri`] parsing errors.
73    InvalidPaymentUri(Cow<'static, str>),
74    /// [`Bip321Uri`] parsing errors.
75    InvalidBip321Uri(Cow<'static, str>),
76    /// [`LightningUri`] parsing errors.
77    InvalidLightningUri(Cow<'static, str>),
78    // `uri::Uri`
79    InvalidUri(Cow<'static, str>),
80    /// [`EmailLikeAddress`] parsing errors.
81    InvalidEmailLike(Cow<'static, str>),
82    InvalidLnurl(Cow<'static, str>),
83    InvalidInvoice(invoice::ParseError),
84    InvalidOffer(offer::ParseError),
85    InvalidBtcAddress(bitcoin::address::ParseError),
86}
87
88impl std::error::Error for Error {}
89
90impl fmt::Display for Error {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match self {
93            Self::InvalidUri(msg) => write!(f, "Invalid URI: {msg}"),
94            Self::InvalidPaymentUri(msg) =>
95                write!(f, "Invalid payment URI: {msg}"),
96            Self::InvalidBip321Uri(msg) =>
97                write!(f, "Invalid 'bitcoin:' URI: {msg}"),
98            Self::InvalidLightningUri(msg) =>
99                write!(f, "Invalid 'lightning:' URI: {msg}"),
100            Self::InvalidEmailLike(msg) =>
101                write!(f, "Invalid BIP353 / Lightning Address: {msg}"),
102            Self::InvalidLnurl(msg) => write!(f, "Invalid LNURL: {msg}"),
103            Self::InvalidInvoice(err) => Display::fmt(err, f),
104            Self::InvalidOffer(err) => Display::fmt(err, f),
105            Self::InvalidBtcAddress(err) =>
106                write!(f, "Invalid on-chain address: {err}"),
107        }
108    }
109}