-
Notifications
You must be signed in to change notification settings - Fork 238
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
Make Errors more "narrow" #811
Conversation
a94ee2a
to
004442c
Compare
Codecov ReportAttention: Patch coverage is
❗ Your organization needs to install the Codecov GitHub app to enable full functionality. Additional details and impacted files@@ Coverage Diff @@
## master #811 +/- ##
==========================================
+ Coverage 60.08% 60.14% +0.05%
==========================================
Files 41 41
Lines 15975 15985 +10
==========================================
+ Hits 9599 9614 +15
+ Misses 6376 6371 -5
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
8fcf6fd
to
a0ab37f
Compare
Sorry for the force pushes, it keeps running my usage example as a doc test and the annotations (like |
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.
Some doc links became broken, that need to be fixed
> cargo doc --all-features
warning: unresolved link to `encoding`
--> src\encoding.rs:22:18
|
22 | /// If feature [`encoding`] is disabled, the EncodingError is always `DecodeError::Utf8`:
| ^^^^^^^^ no item named `encoding` in scope
|
= help: to escape `[` and `]` characters, add '\' before them like `\[` or `\]`
= note: `#[warn(rustdoc::broken_intra_doc_links)]` on by default
warning: unresolved link to `Error::IllFormed`
--> src\reader\buffered_reader.rs:308:75
|
308 | /// If a corresponding [`End`] event is not found, an error of type [`Error::IllFormed`]
| ^^^^^^^^^^^^^^^^ no item named `Error` in scope
warning: unresolved link to `Error::IllFormed`
--> src\reader\slice_reader.rs:88:75
|
88 | /// If a corresponding [`End`] event is not found, an error of type [`Error::IllFormed`]
| ^^^^^^^^^^^^^^^^ no item named `Error` in scope
warning: `quick-xml` (lib doc) generated 3 warnings
I'm fine with the first 3 commits, but I'm unsure about the 4th (other two commits just the follow-ups fixing the compilation errors). I'm not sure that such a fine breakdown of errors will be convenient for work. In most cases, you will not be interested in a specific type of error and it will still be thrown higher, but the presence of many types of errors will complicate such throwing (it's not for nothing that things like anyhow::Error
appeared).
Do you have opposite experience?
You can fix things and make a force push, GitHub UI provides a way to compare between force pushes, so it is fine). I would prefer to have a history without fix-up commits, so it would be great if each commit:
- in a compilable state on CI (which means it is compiled fine with all CI tested combinations of flags, in practice usually check of
cargo test
andcargo test --all-features
is enough) cargo doc --all-features
does not report warnings- each commit updates changelog with the relevant changes (so it is easely to understand later in which commit that change was made)
src/errors.rs
Outdated
impl From<Utf8Error> for Error { | ||
/// Creates a new `Error::NonDecodable` from the given error | ||
impl From<EncodingError> for Error { | ||
/// Creates a new `Error::DecodeError` from the given 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.
You named this variant EncodingError
/// Creates a new `Error::DecodeError` from the given error | |
/// Creates a new [`Error::EncodingError`] from the given error |
Or do the opposite: name the variant DecodeError
. Maybe this is preferred, because this error is possible only when reading
src/encoding.rs
Outdated
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { | ||
match self { | ||
Self::Utf8(e) => Some(e), | ||
#[allow(unreachable_patterns)] |
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.
Why not
#[allow(unreachable_patterns)] | |
#[cfg(feature = "encoding")] |
?
/// | ||
/// If feature [`encoding`] is disabled, the EncodingError is always `DecodeError::Utf8`: | ||
#[derive(Clone, Debug, PartialEq, Eq)] | ||
pub enum EncodingError { |
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.
Need to add #[non_exhaustive]
so the consumers forced to explicitly handle wildcard variant. Otherwise if some other crate in the dependency tree activates the encoding
feature, the crates without wildcard handling and without encoding
feature will fail to compile.
pub enum EncodingError { | |
#[non_exhaustive] | |
pub enum EncodingError { |
src/encoding.rs
Outdated
Self::Utf8(e) => write!(f, "UTF-8 error: {}", e), | ||
#[cfg(feature = "encoding")] | ||
Self::Other(encoding) => write!(f, "Error occured when decoding {}", encoding.name()), |
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.
Make texts start with lower-case letter and unify them. I assume that error is used only when decoding, otherwise it is needed to tweak messages
Self::Utf8(e) => write!(f, "UTF-8 error: {}", e), | |
#[cfg(feature = "encoding")] | |
Self::Other(encoding) => write!(f, "Error occured when decoding {}", encoding.name()), | |
Self::Utf8(e) => write!(f, "cannot decode input using UTF-8: {}", e), | |
#[cfg(feature = "encoding")] | |
Self::Other(encoding) => write!(f, "cannot decode input using {}", encoding.name()), |
src/encoding.rs
Outdated
@@ -1,14 +1,10 @@ | |||
//! A module for wrappers that encode / decode data. | |||
|
|||
use std::borrow::Cow; | |||
use std::{borrow::Cow, str::Utf8Error}; |
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 prefer to not have nested imports:
use std::{borrow::Cow, str::Utf8Error}; | |
use std::borrow::Cow; | |
use std::str::Utf8Error; |
src/name.rs
Outdated
/// Error for when a reserved namespace is set incorrectly. | ||
/// | ||
/// This error returned in following cases: | ||
/// - the XML document attempts to bind `xml` prefix to something other than | ||
/// `http://www.w3.org/XML/1998/namespace` | ||
/// - the XML document attempts to bind `xmlns` prefix | ||
/// - the XML document attempts to bind some prefix (except `xml`) to | ||
/// `http://www.w3.org/XML/1998/namespace` | ||
/// - the XML document attempts to bind some prefix to | ||
/// `http://www.w3.org/2000/xmlns/` | ||
InvalidPrefixBind { |
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 split error into small parts, maybe make a dedicated variant for each listed variant?
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 split error into small parts, maybe make a dedicated variant for each listed variant?
I will add a commit for this soon. Need to understand the code and the linked standard to see which error applies were which may take a bit longer.
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.
Done in commit 8002cc6
src/name.rs
Outdated
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
Self::UnknownPrefix(prefix) => { | ||
f.write_str("Unknown namespace prefix '")?; |
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.
As already mentioned, if we bring order in errors, make all texts with a small letter:
f.write_str("Unknown namespace prefix '")?; | |
f.write_str("unknown namespace prefix '")?; |
src/name.rs
Outdated
f.write_str("'") | ||
} | ||
Self::InvalidPrefixBind { prefix, namespace } => { | ||
f.write_str("The namespace prefix '")?; |
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.
Same here
f.write_str("The namespace prefix '")?; | |
f.write_str("the namespace prefix '")?; |
src/errors.rs
Outdated
$($variant:ident($error:path $(, $inner_type:path)?) $fmt_str:literal),+ $(,)? | ||
) => { | ||
#[derive(Debug)] | ||
#[allow(missing_docs)] |
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 would like to update macro and write a documentation for each generated variant under which circumstances it will be returned. That is not always obvious from the name and description of the inner error.
#[allow(missing_docs)] |
Also, I would prefer to have more traditional syntax for defining enums, in order to in the end the written code will look like:
combined_error! {
/// Doc
// derives
pub enum SpecificError {
/// Variant 1 doc
Variant1(Variant1Error) => "display 1 text",
/// Variant 2 doc
Variant2(Variant2Error) => "display 2 text",
}
}
Please also derive Debug
for each error type.
Compiling the docs was an oversight. Will fix.
I'm mostly interested in not having irrelevant options when calling lower level apis. I will agree that the I'm torn on whether this is better, especially when it's common for If this split beyond
Absolutely. The docs was an oversight and I know that one commit doesn't pass the tests. I will go back and rework all of these commits. |
When the errors are being changed anyway, there's an opportunity to be consistent about whether Error enum variants should be named |
When we declare in API a more wide error that is really could happen, I'm fine to narrow the result type, but introducing new fine-granulated error types for that, I think, would be overkill. The problem is that the API may not be well-established, and with the introduction of validation checks, it may turn out that some functions will return more errors. Some Linux package maintainers already complained, that quick-xml API changes too quickly :).
Yes. So for now please left only the changes from the first 3 commits. Maybe in time the 4th commit also would be welcomed, who knows :)?
|
a0ab37f
to
4bbb94b
Compare
I belive I have fixed all issues from the previous review. The changelog is now incrementally updated every commit and all commits pass I have still included the |
… deserialization error messages More clear and may slightly increase compile time
…e of type Make code more consistent
This mostly allows for decode functions to return a smaller more accurate error
…mplements PartialEq
8002cc6
to
00a0962
Compare
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 polished the PR slightly:
- Normalized all error messages to start from lowercase
- Made error-related code more consistent: removed unnecessary usages of
write!
macro, usedSelf
instead of type name where possible (that two changes was related to the errors so it was logical to made them in that PR) - Implemented
PartialEq
andEq
forNamespaceError
to simplify testing of the methods returning results with that error - That allowed to change tests from manual matching on result to use
assert_eq!
which will provide nice diff if failed - You forgot to return
Some
fromError::source
forError::Namespace
-- fixed - Used tuple form instead of struct form for
NamespaceError
errors. Usually struct form with one field is not used - Removed
BangType
from changelog. It is internal type not visible to users
Thanks! |
This was discussed in #810 here.
This splits up error types so that there is almost one type for each module, which narrows the amount of error variants that is returned from each function.
For the functions where two or more error types may be returned the new
combined_error!
macro is used to create a new errorenum
that holds these variants with most errorimpl
's automatically done.The old
errors::Error
is still present and public. All other new errors implementFrom<_> for errors::Error
so that the providederrors::Result
type can still be used to try (?
) any function from this crate.I haven't spent much time adding/editing docs for these changes since most of them still apply without changes. Maybe the docs for
errors::Error
should be changed to make it clear that it will not be given out as an error from anywhere directly. There are also some names of the new error types that might need changing