-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Change error-chain
to thiserror
#88
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for giving this library some love! I'm all for getting rid of error-chain
, but I do have some feedback and comments about exactly how it's done. See individual comments.
Error design is a debated topic. Personally I prefer that libraries use handwritten* errors, are #[non_exhaustive]
.
*Keeps down number of dependencies for not that much extra boilerplate
pub info: Option<ErrorInfo>, | ||
#[source] | ||
pub source: ErrorSource, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have not really seen this exact way of desiging errors before. Do you have references to other libraries using this style? I mean splitting the error into a struct plus two enums and having the latter be public struct members etc.
Given how everything is public and the enums are not #[non_exhaustive]
adding any new error variant is a breaking change, which is not ideal IMO.
This was discussed at Mullvad recently and we ended up with: mullvad/udp-over-tcp#57. I'd much prefer something along those lines.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have references to other libraries using this style?
No. I guess I've just invented it myself. Just saved the original logic as it was before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the idea behind having info
as a separate thing here? Absolutely everything regarding the internal details of these errors are exposed, making it a breaking change to change anything in it. I'd advise you to take a look at the PR I linked above where we did error improvements.
There is no need to keep the logic similar to how the errors were before. That is really old and outdated code, created by a very outdated error library. So it's probably not a good source of inspiration.
src/lib.rs
Outdated
} | ||
self.source.fmt(f) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including the source of the error in the display impl of it is not recommended. It makes printing a full error chain very redundant. Usually something like:
Error: Failed to start: Permission denied
Caused by: Permission denied
Which just looks bad. Please see this guideline PR for more context: rust-lang/api-guidelines#210
Cargo.toml
Outdated
scopeguard = "1.0" | ||
uuid = { version = "1", features = ["v4"] } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please separate the dependency upgrades not related to swapping out error-chain
to a separate PR. I prefer if PRs are as focused as possible on one thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll have to wait for the merge then. That's actually very annoying if you accept only one thing per PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have issues/complications it's going to be significantly easier to find what the cause is if changes are separated into smaller logical chunks. Also easier to revert single things instead of a blob of unrelated things. Also way easier to review and reason about.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you submit small easy to review PRs I can review and merge them fast.
Fixed |
pub info: Option<ErrorInfo>, | ||
#[source] | ||
pub source: ErrorSource, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the idea behind having info
as a separate thing here? Absolutely everything regarding the internal details of these errors are exposed, making it a breaking change to change anything in it. I'd advise you to take a look at the PR I linked above where we did error improvements.
There is no need to keep the logic similar to how the errors were before. That is really old and outdated code, created by a very outdated error library. So it's probably not a good source of inspiration.
src/lib.rs
Outdated
} | ||
Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means that an error with no info
prints as nothing (empty string). That's not good. An error should never have an empty display representation, there must always be some description of what the error means.
Fixed. Is it better? |
Still don't understand why it's bad |
399165f
to
89a14c7
Compare
Again, see mullvad/udp-over-tcp#57. If every aspect of the error is part of the public API then every small "internal" change becomes a breaking change. Let's say we wanted to change If we don't expose details that a user usually won't ever need to rely on, the API surface both becomes cleaner, and we are freer to change the internals without breaking stuff. |
I see the reasoning, but I don't understand why would you lose a part of the error. For |
If that is "internal", but internally we don't use it, maybe we actually don't need it then? But it's used in the display of the error message. That's why I would like to leave it as is. So a user would get more information about the error. |
@@ -128,14 +128,16 @@ impl FilterRule { | |||
"StatePolicy {:?} and protocol {:?} are incompatible", | |||
state_policy, proto | |||
); | |||
bail!(ErrorKind::InvalidRuleCombination(msg)); | |||
Err(Error::InvalidRuleCombination(msg)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be better to return Error::InvalidRuleCombination(cause)
as opposed to a pre-reformatted string msg? where cause
could be:
enum InvalidRuleCause {
StatePolicyAndProtocol(StatePolicy, Proto)
AddrFamily(AddrFamily, AddrFamily)
}
Also pfctl::FilterRuleBuilder::build()
maps to InvalidRuleCombination
which is rather odd. I think that root crate::Error
should have a separate variant for that, perhaps:
BuildFilterRule(#[source] String) // error_derive returns string?
Alternatively, which seems simpler (better) to me, three new variants can be added into crate::Error
and InvalidRuleCombination
removed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think the end user needs AddrFamily
, because it can be incompatible only when they don't match. It can be a new Error
variant something like: IncompatibleAddrFamily
.
I can create a separate variant for validate_state_policy
with StatePolicy
and Proto
enums, if @faern would approve this.
And also separate variants for FilterRuleBuilder
and RedirectRuleBuilder
.
@faern what do you think?
src/lib.rs
Outdated
#[error("Cstr not null terminated")] | ||
CstrNotTerminated, | ||
|
||
#[error("Anchor does not exist")] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type conversion error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/lib.rs
Outdated
InvalidPortRange, | ||
|
||
#[error("String does not fit destination")] | ||
StrCopyNotFits, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename it to InsufficientStringBufferLength
. Description should state the same, does fit "destination" is really mysterious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
src/lib.rs
Outdated
StrCopyNotFits, | ||
|
||
#[error("String has null byte")] | ||
StrCopyNullByte, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rename it to StringContainsNullByte
and describe as "String should not contain null byte"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Unless I misunderstood you. #[derive(Debug, Error)]
enum ApplyTcpOptionsError {
RecvBuffer(#[source] io::Error),
SendBuffer(#[source] Io::Error),
#[cfg(target_os = "linux")]
Mark(#[source] nix::Error),
TcpNoDelay(#[source] io::Error),
} What you did there basically you told consumer that whatever you do internally is not their business because It doesn't help you either if you decide to remove some
In your case if anyone matched against The PR above also introduces I'd personally be in favor of not going that route. I don't know who your users are in udp-over-tcp and what level of detail they need when it comes to error matching, but in pfctl-rs we do not handle 99% of ioctl errors beyond a few that we map to What do you really propose to do here? |
Thanks for wanting to contribute! However, we merged a different solution to this issue over in #107. The main issue in this PR was how the entire API of the error was still public. Part of wanting to get rid of |
Changed
error-chain
tothiserror
, and updated dependencies.Maybe you would also want to remove
error-chain
from examples and tests.Closes #66
This change is