diff --git a/payjoin-cli/src/app/config.rs b/payjoin-cli/src/app/config.rs index dbb51a478..9b072ae94 100644 --- a/payjoin-cli/src/app/config.rs +++ b/payjoin-cli/src/app/config.rs @@ -127,6 +127,7 @@ impl Config { "BIP77 (v2) selected but v2 feature not enabled".to_string(), )); } + _ => return Err(ConfigError::Message("Unsupported payjoin version".to_string())), } config = handle_subcommands(config, cli)?; @@ -235,6 +236,7 @@ impl Config { "BIP77 (v2) selected but v2 feature not enabled".to_string(), )); } + _ => return Err(ConfigError::Message("Unsupported payjoin version".to_string())), } if config.version.is_none() { diff --git a/payjoin/src/core/into_url.rs b/payjoin/src/core/into_url.rs index 9d45755a5..c8f5c9aaf 100644 --- a/payjoin/src/core/into_url.rs +++ b/payjoin/src/core/into_url.rs @@ -1,6 +1,7 @@ use crate::core::{Url, UrlParseError}; #[derive(Debug, PartialEq, Eq)] +#[non_exhaustive] pub enum Error { BadScheme, ParseError(UrlParseError), diff --git a/payjoin/src/core/mod.rs b/payjoin/src/core/mod.rs index ec64e9963..1bb912a75 100644 --- a/payjoin/src/core/mod.rs +++ b/payjoin/src/core/mod.rs @@ -25,7 +25,6 @@ pub use uri::{PjParam, PjParseError, PjUri, Uri, UriExt}; pub(crate) mod error_codes; pub(crate) mod output_substitution; -#[cfg(feature = "v1")] pub use output_substitution::OutputSubstitution; #[cfg(feature = "v2")] diff --git a/payjoin/src/core/persist.rs b/payjoin/src/core/persist.rs index 969513120..5961da6cb 100644 --- a/payjoin/src/core/persist.rs +++ b/payjoin/src/core/persist.rs @@ -69,6 +69,7 @@ impl PersistActions { /// Handles cases where the transition either succeeds with a final result that ends the session, or hits a static condition and stays in the same state. /// State transition may also be a fatal error or transient error. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeSuccessTransitionWithNoResults( Result, Rejection>, ); @@ -156,6 +157,7 @@ where } /// A transition that can result in a state transition, fatal error, or successfully have no results. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeFatalTransitionWithNoResults( Result, Rejection>, ); @@ -240,6 +242,7 @@ where } /// A transition that can be either fatal, transient, or a state transition. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeFatalTransition( pub(crate) Result, Rejection>, ); @@ -310,6 +313,7 @@ where /// A transition that can result in a state transition or a transient error. /// Fatal errors cannot occur in this transition. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeTransientTransition( Result, RejectTransient>, ); @@ -362,6 +366,7 @@ where } /// A transition that always results in a state transition. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct NextStateTransition(AcceptNextState); impl NextStateTransition { @@ -400,6 +405,7 @@ impl NextStateTransition { /// No error path exists. Both outcomes are successful from the protocol's point /// of view. The choice is determined by the source typestate's internal data, /// not by the caller. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeTerminalTransition(MaybeTerminalOutcome); impl MaybeTerminalTransition { @@ -446,6 +452,7 @@ impl MaybeTerminalTransition { /// Fatal outcomes still persist an event. When the fatal outcome advances, the /// saved event keeps the session live for replay while the caller receives the /// fatal protocol error. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct MaybeTerminalSuccessTransition( MaybeTerminalSuccessOutcome, ); @@ -531,6 +538,7 @@ where /// being persisted. This lets callers receive derived data (e.g. a fallback /// transaction) through the same `.save()` call pattern used by every other /// transition type. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub struct TerminalTransition(Event, T); impl TerminalTransition { @@ -557,6 +565,7 @@ impl TerminalTransition { /// A transition that can result in a succession completion, fatal error, or transient error. /// The transition can also result in no state change. +#[must_use = "a transition must be persisted with .save() to advance the session"] pub enum MaybeFatalOrSuccessTransition { Success(Event), NoResults(CurrentState), diff --git a/payjoin/src/core/receive/error.rs b/payjoin/src/core/receive/error.rs index 8e0d31e8a..6048f86fe 100644 --- a/payjoin/src/core/receive/error.rs +++ b/payjoin/src/core/receive/error.rs @@ -57,6 +57,7 @@ impl error::Error for Error { /// 4. Provide errors according to BIP-78 JSON error specifications for return /// after conversion into [`JsonReply`] #[derive(Debug)] +#[non_exhaustive] pub enum ProtocolError { /// Error arising from validation of the original PSBT payload OriginalPayload(PayloadError), diff --git a/payjoin/src/core/receive/v2/session.rs b/payjoin/src/core/receive/v2/session.rs index 57f4e133e..f24d6ef3c 100644 --- a/payjoin/src/core/receive/v2/session.rs +++ b/payjoin/src/core/receive/v2/session.rs @@ -180,6 +180,7 @@ impl SessionHistory { // Represents the status of a session that can be inferred from the information in the session // event log. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum SessionStatus { Active, Expired, diff --git a/payjoin/src/core/send/v2/session.rs b/payjoin/src/core/send/v2/session.rs index fc6955951..f38938739 100644 --- a/payjoin/src/core/send/v2/session.rs +++ b/payjoin/src/core/send/v2/session.rs @@ -143,6 +143,7 @@ impl SessionHistory { /// Represents the status of a session that can be inferred from the information in the session /// event log. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum SessionStatus { Expired, Active, diff --git a/payjoin/src/core/url.rs b/payjoin/src/core/url.rs index 7628bf04a..1e6c52d41 100644 --- a/payjoin/src/core/url.rs +++ b/payjoin/src/core/url.rs @@ -143,6 +143,7 @@ impl<'a> UrlQueryPairs<'a> { /// /// Re-exported at the crate root as `UrlParseError`. #[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] pub enum ParseError { /// The authority section had no host between `://` and the path. EmptyHost, diff --git a/payjoin/src/core/version.rs b/payjoin/src/core/version.rs index 234647617..a0c7cbaaf 100644 --- a/payjoin/src/core/version.rs +++ b/payjoin/src/core/version.rs @@ -21,6 +21,7 @@ use serde::{Deserialize, Deserializer, Serialize, Serializer}; /// and to match the expected wire format. #[repr(u8)] #[derive(Clone, Copy, PartialEq, Eq)] +#[non_exhaustive] pub enum Version { /// BIP 78 Payjoin One = 1, diff --git a/payjoin/src/directory.rs b/payjoin/src/directory.rs index a82f70153..66166bff8 100644 --- a/payjoin/src/directory.rs +++ b/payjoin/src/directory.rs @@ -42,11 +42,30 @@ impl std::fmt::Display for ShortId { } #[derive(Debug)] +#[non_exhaustive] pub enum ShortIdError { DecodeBech32(bitcoin::bech32::primitives::decode::CheckedHrpstringError), IncorrectLength(std::array::TryFromSliceError), } +impl std::fmt::Display for ShortIdError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ShortIdError::DecodeBech32(e) => write!(f, "Failed to decode short ID: {e}"), + ShortIdError::IncorrectLength(e) => write!(f, "Short ID has an incorrect length: {e}"), + } + } +} + +impl std::error::Error for ShortIdError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + ShortIdError::DecodeBech32(e) => Some(e), + ShortIdError::IncorrectLength(e) => Some(e), + } + } +} + impl std::convert::From for ShortId { fn from(h: bitcoin::hashes::sha256::Hash) -> Self { bitcoin::hashes::Hash::as_byte_array(&h)[..8]