You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
error-stack support for multiple related errors #727
I have been checking out error-stack lately, and it seems very promising, great work! For my specific usecase I would want report multiple errors at once, while not natively supported by the std::error::Error trait, crates like miette have the concept of related errors.
From my investigation into the library error-stack doesn't currently support this feature, is this correct? Are there any plans for supporting such a thing in the future? or would you be open for a PR implementing something like this? I think something like this would be a great addition to the library!
What I'd love to have is a DAG like the diagram below:
For the "source" of the error (with compatibility to std::err::Error) we could either only take the left hand side, or linearize the resulting graph through topological sorting.
Just as a reference, the topological sorting using DFS of the graph shown above would result in the following:
flowchart BT;
a{{std::io::Error}} --> b{{crate1::Error}}
b --> c[crate2::Context2]
c --> d[crate3::Context2]
d --> e{{std::io::Error}}
e --> f[crate2::Context]
f --> g[crate3::Context]
g --> h[crate4::Context]
This is a pretty good scenario and we haven't thought about this yet. However, it's possible to attach any data to a Report, even other reports like:
let report_a = Report::new(...);let report_b = Report::new(...);
report_a.attach(report_b);
Later it's then possible to call report_a.request_ref() or even for each frame in report_a.frames() to call frame.request_ref() (Note that requesting currently requires a nightly compiler). I think this is almost the behavior you are looking for?
However, there is one issue: Report has a generic for the current Context, so you would need to specify which kind of Report you want to get back.
Do you have a specific solution in mind? Maybe it makes sense to talk about a potential idea before opening a PR.
My suggestion would be to have a new trait: ChangeContext (or named differently), which is implemented for Report and all tuple sizes, where each element must be a Report, but their context doesn't need to be the same.
The thing that would need to be changed is that Frame would have multiple sources.
Traversing the "graph" via .frames() would be more challenging, and I don't have a good way of doing that just yet.
Displaying the error would also probably involve further indentation on a "cross-road," e.g., where two reports/frames are joined.
but to your earlier point, the provider API would kind of make sense, but I would need to know the context type in advance, then traverse through, which is doable, but a lot of pain
I had a thought when traversing frames, we could, in theory, use BFS or DFS (or the topological sorting), but this would mean that we wouldn't be able to guarantee that every frame before a parent of the previous one.
I thought about this for a long time and I think I came up with a possible solution.
First of all, the ReportImpl abstraction should be removed, so Report refers to Frame directly. This can be done by not storing the backtrace/span trace on Report but as an attachment, if they were captured. I think this moves both to a less "special" spot and make the behavior for the user more intuitive. I think this should be done regardless of related errors.
Now, a Report is only a Frame anymore. We now have two options (method naming TBD):
Add a Report::attach_report method (which can be merged with attach once specialization is a thing):
Stores the Frames of the attached Report inline of self
Add a Report::combine method:
Stores the Frames of the attached Report next to the current frame, e.g. by making Frame::source a Vec<Frame>
I'd prefer the second option, especially as it's not special casing Report when attaching but "simply" add a feature. For printing, I have a few thoughts. The structure is now a tree (but we keep pushing onto the root of it), Backtrace and SpanTrace as attachments instead could print the number of frames: Printing could happen like this:
Current context
| attachment
| attachment
|\
| Another context
| | attachment
| |
| Other context
| | attachment
| | backtrace with 15 frames
|
Other context
| attachment
| backtrace with 12 frames
| span trace with 3 frames
Activity
indietyp commentedon Jul 1, 2022
For the "source" of the error (with compatibility to
std::err::Error) we could either only take the left hand side, or linearize the resulting graph through topological sorting.indietyp commentedon Jul 1, 2022
Just as a reference, the topological sorting using DFS of the graph shown above would result in the following:
flowchart BT; a{{std::io::Error}} --> b{{crate1::Error}} b --> c[crate2::Context2] c --> d[crate3::Context2] d --> e{{std::io::Error}} e --> f[crate2::Context] f --> g[crate3::Context] g --> h[crate4::Context]TimDiekmann commentedon Jul 1, 2022
Hi @indietyp and thanks for filing this!
Generally, Pull Request are highly appreciated!
This is a pretty good scenario and we haven't thought about this yet. However, it's possible to attach any data to a
Report, even other reports like:Later it's then possible to call
report_a.request_ref()or even for eachframe in report_a.frames()to callframe.request_ref()(Note thatrequesting currently requires a nightly compiler). I think this is almost the behavior you are looking for?However, there is one issue:
Reporthas a generic for the currentContext, so you would need to specify which kind ofReportyou want to get back.Do you have a specific solution in mind? Maybe it makes sense to talk about a potential idea before opening a PR.
indietyp commentedon Jul 1, 2022
My suggestion would be to have a new trait:
ChangeContext(or named differently), which is implemented forReportand all tuple sizes, where each element must be aReport, but their context doesn't need to be the same.The thing that would need to be changed is that
Framewould have multiple sources.Traversing the "graph" via
.frames()would be more challenging, and I don't have a good way of doing that just yet.Displaying the error would also probably involve further indentation on a "cross-road," e.g., where two reports/frames are joined.
ChangeContext::change_context<T: Context>(self, context: T) -> Report<T>if
a: Report<C1>,b: Report<C2>,c: C3then:(a, b).change_context(c) -> Report<C3>indietyp commentedon Jul 1, 2022
but to your earlier point, the provider API would kind of make sense, but I would need to know the context type in advance, then traverse through, which is doable, but a lot of pain
indietyp commentedon Jul 1, 2022
I had a thought when traversing frames, we could, in theory, use BFS or DFS (or the topological sorting), but this would mean that we wouldn't be able to guarantee that every frame before a parent of the previous one.
TimDiekmann commentedon Jul 3, 2022
I thought about this for a long time and I think I came up with a possible solution.
First of all, the
ReportImplabstraction should be removed, soReportrefers toFramedirectly. This can be done by not storing the backtrace/span trace onReportbut as an attachment, if they were captured. I think this moves both to a less "special" spot and make the behavior for the user more intuitive. I think this should be done regardless of related errors.Now, a
Reportis only aFrameanymore. We now have two options (method naming TBD):Report::attach_reportmethod (which can be merged withattachonce specialization is a thing):Stores the
Frames of the attachedReportinline ofselfReport::combinemethod:Stores the
Frames of the attachedReportnext to the current frame, e.g. by makingFrame::sourceaVec<Frame>I'd prefer the second option, especially as it's not special casing
Reportwhen attaching but "simply" add a feature. For printing, I have a few thoughts. The structure is now a tree (but we keep pushing onto the root of it),BacktraceandSpanTraceas attachments instead could print the number of frames: Printing could happen like this:What do you think about that?