• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

payjoin / rust-payjoin / 29448737091

15 Jul 2026 08:33PM UTC coverage: 86.187% (+0.2%) from 85.938%
29448737091

Pull #1707

github

web-flow
Merge bb5eb7fc2 into 1da296b10
Pull Request #1707: Prepare release-ready C# NuGet package workflow

13758 of 15963 relevant lines covered (86.19%)

345.48 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.78
/payjoin/src/core/error.rs
1
use std::fmt::Debug;
2
use std::{error, fmt};
3

4
#[derive(Debug)]
5
pub struct ImplementationError(Box<dyn error::Error + Send + Sync>);
6

7
impl ImplementationError {
8
    pub fn new(e: impl error::Error + Send + Sync + 'static) -> Self {
6✔
9
        ImplementationError(Box::new(e))
6✔
10
    }
6✔
11
}
12

13
impl fmt::Display for ImplementationError {
14
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { std::fmt::Display::fmt(&self.0, f) }
19✔
15
}
16

17
impl error::Error for ImplementationError {
18
    fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(self.0.as_ref()) }
×
19
}
20

21
impl PartialEq for ImplementationError {
22
    fn eq(&self, _: &Self) -> bool { false }
×
23
}
24

25
impl Eq for ImplementationError {}
26

27
impl From<Box<dyn error::Error + Send + Sync>> for ImplementationError {
28
    fn from(e: Box<dyn error::Error + Send + Sync>) -> Self { ImplementationError(e) }
48✔
29
}
30

31
impl From<&str> for ImplementationError {
32
    fn from(e: &str) -> Self {
48✔
33
        let error = Box::<dyn error::Error + Send + Sync>::from(e);
48✔
34
        ImplementationError::from(error)
48✔
35
    }
48✔
36
}
37
/// Errors that can occur when replaying a session event log
38
#[cfg(feature = "v2")]
39
#[derive(Debug)]
40
pub struct ReplayError<SessionState, SessionEvent>(InternalReplayError<SessionState, SessionEvent>);
41

42
#[cfg(feature = "v2")]
43
impl<SessionState: Debug, SessionEvent: Debug> std::fmt::Display
44
    for ReplayError<SessionState, SessionEvent>
45
{
46
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18✔
47
        use InternalReplayError::*;
48
        match &self.0 {
18✔
49
            NoEvents => write!(f, "No events found in session"),
×
50
            InvalidEvent(event, session) => match session {
8✔
51
                Some(session) => write!(f, "Invalid event ({event:?}) for session ({session:?})",),
×
52
                None => write!(f, "Invalid first event ({event:?}) for session",),
8✔
53
            },
54
            InvalidEventPayload(event, reason) =>
4✔
55
                write!(f, "Invalid event payload ({event:?}): {reason}"),
4✔
56
            Expired(time, _) => write!(f, "Session expired at {time:?}"),
6✔
57
            PersistenceFailure(e) => write!(f, "Persistence failure: {e}"),
×
58
        }
59
    }
18✔
60
}
61
#[cfg(feature = "v2")]
62
impl<SessionState: Debug, SessionEvent: Debug> std::error::Error
63
    for ReplayError<SessionState, SessionEvent>
64
{
65
}
66

67
#[cfg(feature = "v2")]
68
impl<SessionState: Debug, SessionEvent: Debug> From<InternalReplayError<SessionState, SessionEvent>>
69
    for ReplayError<SessionState, SessionEvent>
70
{
71
    fn from(e: InternalReplayError<SessionState, SessionEvent>) -> Self { ReplayError(e) }
20✔
72
}
73

74
#[cfg(feature = "v2")]
75
impl<SessionState, SessionEvent> ReplayError<SessionState, SessionEvent> {
76
    /// Returns `true` if the event log could not be replayed because the
77
    /// session has expired.
78
    pub fn is_expired(&self) -> bool { matches!(self.0, InternalReplayError::Expired(..)) }
4✔
79

80
    /// Returns the fallback transaction that should be broadcast when the
81
    /// session has expired, if one is recoverable from the event log.
82
    ///
83
    /// Returns `None` for non-expired errors, or for an expired session that
84
    /// never produced a fallback transaction (e.g. a receiver that expired
85
    /// before receiving the sender's original proposal).
86
    pub fn expiry_fallback_tx(&self) -> Option<&bitcoin::Transaction> {
3✔
87
        match &self.0 {
3✔
88
            InternalReplayError::Expired(_, tx) => tx.as_ref(),
3✔
89
            _ => None,
×
90
        }
91
    }
3✔
92
}
93

94
#[cfg(feature = "v2")]
95
#[derive(Debug)]
96
pub(crate) enum InternalReplayError<SessionState, SessionEvent> {
97
    /// No events in the event log
98
    NoEvents,
99
    /// Invalid initial event
100
    InvalidEvent(Box<SessionEvent>, Option<Box<SessionState>>),
101
    /// Event payload failed validation
102
    InvalidEventPayload(Box<SessionEvent>, String),
103
    /// Session is expired. Carries the recoverable fallback transaction, if any.
104
    Expired(crate::time::Time, Option<bitcoin::Transaction>),
105
    /// Application storage error
106
    PersistenceFailure(ImplementationError),
107
}
108

109
#[cfg(all(test, feature = "v2"))]
110
mod tests {
111
    use super::*;
112
    use crate::time::Time;
113

114
    #[test]
115
    fn replay_error_is_expired() {
1✔
116
        let expired: ReplayError<(), ()> =
1✔
117
            ReplayError(InternalReplayError::Expired(Time::now(), None));
1✔
118
        assert!(expired.is_expired());
1✔
119
        assert!(expired.expiry_fallback_tx().is_none());
1✔
120

121
        let other: ReplayError<(), ()> = ReplayError(InternalReplayError::NoEvents);
1✔
122
        assert!(!other.is_expired());
1✔
123
    }
1✔
124
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc