Conversation
548e171 to
1f95bbc
Compare
| impl From<String> for ImplementationError { | ||
| fn from(value: String) -> Self { | ||
| Self(value.into()) | ||
| } | ||
| } |
There was a problem hiding this comment.
ImplementationError can be derived from any String. I believe this is fine and similar to how anyhow works.
There was a problem hiding this comment.
anyhow::Error seems to impl From<E: StdError + Send + Sync + 'static> seems like that's not the same as From but String just qualifies
There was a problem hiding this comment.
The problem (iiuc) is that a blanket implementation for From<E> also covers the case where E = ImplementationError, which results in:
error[E0119]: conflicting implementations of trait `From<ImplementationError>` for type `ImplementationError`
--> src/receive/error.rs:77:1
|
77 | / impl<E> From<E> for ImplementationError
78 | | where
79 | | E: std::error::Error + Send + Sync + 'static,
| |_________________________________________________^
|
= note: conflicting implementation in crate `core`:
- impl<T> From<T> for T;
I think anyhow gets around this with the anyhow_no_core_error macro but can't find implementation details for the macro.
A potential workaround would be to implement and use a pub fn new<E>(error: E) constructor instead of from.
There was a problem hiding this comment.
seems like anyhow_no_core_error is a feature, not a macro. And when that feature is disabled this happens
I have an odd feeling this will bite us but I'm not sure what to do about it
There was a problem hiding this comment.
PjParseError derives from payjoin::Uri::from_str(uri.as_str()) which returns a bitcoin_uri::de::Error<payjoin::PjParseError>>, but bitcoin_uri is an inner dependency of payjoin so we can't implement From for that error type. The current hack turns the error into a string and that string into a PjParseError.
There was a problem hiding this comment.
We should almost definitely be wrapping bitcoin_uri::de::Error<payjoin::PjParseError>> in a payjoin type.
There was a problem hiding this comment.
You mean wrap it in rust-payjoin?
There was a problem hiding this comment.
PjNotSupported is also interesting because check_pj_supported returns a Box<bitcoin_uri::Uri<'a>> for the error type, which doesn't implement std::Error. The same string hack is used to wrap this error type.
There was a problem hiding this comment.
I think this is passable. We'll see when doing integrations.
It was refactored in payjoin/rust-payjoin@2041f54 and is now now a simple struct containing a well-known error message.
The receive::Error type was previously overloaded to map address parse errors. Instead, take a parsed address as parameter and let the implementer handle parsing/network validation.
receive::Error was previously overloaded to map IntoUrlError. Instead, match the return signature of Receiver::new by exposing a IntoUrlError wrapper and returning it as the error type.
1f95bbc to
4d1ef52
Compare
DanGould
left a comment
There was a problem hiding this comment.
A few comments but overall looking much better than what we had before
There was a problem hiding this comment.
I think this is passable. We'll see when doing integrations.
There was a problem hiding this comment.
We should almost definitely be wrapping bitcoin_uri::de::Error<payjoin::PjParseError>> in a payjoin type.
| Error, ImplementationError, InputContributionError, OutputSubstitutionError, ReplyableError, | ||
| SelectionError, SerdeJsonError, | ||
| }; | ||
| use crate::uri::error::IntoUrlError; |
There was a problem hiding this comment.
I can't help but wonder if we ought to have BadScheme, ParseError variants in bindings. Although I don't believe further processing beyond display and debug are necessary in the bindings, which suggests we don't need the variants.
There was a problem hiding this comment.
Agreed on leaving it opaque unless we find a need for it.
|
|
||
| /// The top-level error type for the payjoin receiver | ||
| #[derive(Debug, PartialEq, Eq, thiserror::Error)] | ||
| #[non_exhaustive] |
There was a problem hiding this comment.
is this necessarily and purposefully non_exhaustive and not PartialEq and Eq? Or was this an oversight?
I do think partialEq would require a manual implementation because of the Arc but non_exhaustive seems to work fine.
There was a problem hiding this comment.
non_exhaustive seems unnecessary because we map any unexpected inner type to Error::Unexpected, so there's a catch-all in place of the non_exhaustive pattern.
PartialEq and Eq were removed because of the Arcs. Most of the internal rust-payjoin error types don't implement PartialEq/Eq so I'm not sure we can even implement them manually except with string matching?
There was a problem hiding this comment.
But might we change this signature to have another variant if some Unexpected error becomes expected? e.g. with v3? In that case, without non_exhaustive we'd make an API break
There was a problem hiding this comment.
I reckon if the inner types were Eq we could compare the (*arctype).clone()s
| #[derive(Debug, thiserror::Error)] | ||
| #[error(transparent)] | ||
| #[cfg_attr(feature = "uniffi", derive(uniffi::Object))] | ||
| pub struct ReplyableError(#[from] receive::ReplyableError); |
There was a problem hiding this comment.
We lost PartialEq and Eq here too. It seems that non_exhaustive is preserved through the wrapper. I suppose OK if our inner types aren't Eq (they should be) and we can update when they are.
| impl From<String> for ImplementationError { | ||
| fn from(value: String) -> Self { | ||
| Self(value.into()) | ||
| } | ||
| } |
There was a problem hiding this comment.
anyhow::Error seems to impl From<E: StdError + Send + Sync + 'static> seems like that's not the same as From but String just qualifies
|
|
||
| /// Extract an OHTTP Encapsulated HTTP POST request to return | ||
| /// a Receiver Error Response | ||
| pub fn extract_err_req( | ||
| &self, | ||
| err: &JsonReply, | ||
| ohttp_relay: String, | ||
| ) -> Result<(Request, ClientResponse), SessionError> { | ||
| self.0 | ||
| .clone() | ||
| .extract_err_req(&err.clone().into(), ohttp_relay) | ||
| .map(|(req, ctx)| (req.into(), ctx.into())) | ||
| .map_err(Into::into) | ||
| } | ||
|
|
||
| /// Process an OHTTP Encapsulated HTTP POST Error response | ||
| /// to ensure it has been posted properly | ||
| pub fn process_err_res( | ||
| &self, | ||
| body: &[u8], | ||
| context: &ClientResponse, | ||
| ) -> Result<(), SessionError> { | ||
| self.0.clone().process_err_res(body, context.into()).map_err(Into::into) | ||
| } |
There was a problem hiding this comment.
We should have a test to make sure this works but that's ok in integration imo
There was a problem hiding this comment.
Note there's a extract_err_req unit test in rust-payjoin but no tests for process_err_res, we should cover them in integration tests too.
Opaque error types can be majorly simplified by declaring struct wrappers, then using the #[from] and #[transparent] macros from thiserror to eliminate boilerplate.
Expose the extract_err_req and process_err_res methods on Receiver to return replyable errors to the sender. Expose JsonReply wrapper and relevant From implementations to allow conversions from bound error types into JSON.
4d1ef52 to
5e90125
Compare
|
Added non_exhaustive back to |
This PR addresses #59, #68, #69, and to a lesser extent #70.
I strove to replace all the opaque error types that were previously
struct { msg: String }into the much cleaner and less boilerplatestruct WrapperError(#[from] payjoin::InnerError)with#[error(transparent)]. There are a few stragglers that I'd like to address but don't have a clear way forward yet, so I highlighted those in PR comments.