-
-
Notifications
You must be signed in to change notification settings - Fork 57
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
refactor(errors)!: added proper error handling #262
base: main
Are you sure you want to change the base?
Conversation
for the CLI. Fixed a few parsing bugs.
https://github.com/typst/hayagriva/actions/runs/12382653188/job/34563808908?pr=262 Executing HAYAGRIVA_ARCHIVER_UPDATE=1 cargo test --features csl-json fixes the error. I have no idea if it's something outdated, or it's like a snapshot or something. |
Another question that I have is whether it is good to move all error types into the I like how I printed nested errors in Dioxus with anyhow and |
Please undo the commit and rebase on main, I've updated it now. (We've been meaning to switch this to a weekly workflow instead of something that runs alongside usual CI, but haven't found time for that yet...) |
Is this really necessary? The updated file has the same SHA256 and SHA512 sum. So merging this to main won't make a difference. |
It helps me while reviewing so I ensure your PR isn't doing more than it should. |
Removed `Debug::fmt` usage.
5d177ab
to
7ecb23c
Compare
Fair enough. |
There is a good snippet in https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations: use anyhow::{Context, Result};
fn main() {
if let Err(err) = try_main() {
eprintln!("ERROR: {}", err);
err.chain().skip(1).for_each(|cause| eprintln!("because: {}", cause));
std::process::exit(1);
}
}
fn try_main() -> Result<()> {
...
} The main point is nested function call. The |
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 think this is a step in the right direction, though I'd have appreciated some further discussion before doing all of this work. 😅
It's true that it used to rely on .expect
and the error messages produced by that aren't nice, but I believe your changes made the code more complex without much benefit, as well as unnecessarily affecting parts unrelated to the CLI (particularly io
).
I believe we could try to do it, initially, as such:
- Create a separate function which returns a
Result
, running it frommain
. This allows using?
to propagate and automatically convert errors. - This function would return
Result<(), CliError>
whereCliError
can be defined directly onmain.rs
. This would be helpful as then we can add a method.exit_code()
which returns the correct exit code. The main function can then call it to determine which ExitCode to return, as well as what to print. (See remark about exit codes below.) - We can implement
From<&str>
onCliError
to allow returningResult<..., &'static str>
onretrieve_assets
. Thenretrieve_assets(...)?
would also work without a lot of verbosity. CliError
will have a few variants for common CLI errors with specific exit codes, and maybe a genericOther
variant with just aString
. Conversion would be easy throughFrom
, allowing us to reuseformat!
.- Please use
map_err
with?
instead of matching withOk(v) => v
. Though I don't believe you will have to do that too often with this proposed architecture.
I think this should be enough. Changes to files other than main.rs
would then be dropped.
P.S. Regarding exit codes, I'll try to confirm their purpose with the team to avoid any confusion, but we can preserve them for now.
It was very spontaneous and was done in one (very long) coding session (basically one day). At some point it was too late to back out, so as a result it is much easier to just make a PR rather than stalling and spending some time on prior discussions. But now it has become the reason to discuss all this. There is a lot to cover, so I'll do that later. |
Today I discovered a new issue with Typst + Hayagriva + CSL. I thought of installing the Hayagriva CLI to test what's wrong, but then I was confused by the order and requiredness of the CLI arguments. But then eventually I hit a panic with:
hayagriva bib.yaml reference --select . --csl bib-style.csl
And with backtrace I only was able to find that this is the problem:
hayagriva/src/main.rs
Lines 242 to 252 in 38a56c8
To cut the story short, the problem was with coercing the CLI argument to
&str
because ofSelector::parse()
which also panicked withtry_get_one
. So the solution is to getString
instead. Solved. Thought I would make a quick PR. But then I also tried this:And they both produced different panic errors. And indeed, throughout the
main.rs
(and not only there) there are a ton of.expect()
. This was shocking to me, especially compared to Typst. So one thing lead to another and here we are.I asked the rustaceans a few pointers on various things and this is the more or less final result. My main concern is the return type of public functions in
io
module. For super easy error handling inmain()
I had to convert to the final error right away (also one of the advices from the community). Which can go both ways: either we don't touch the public API and revert some changes, or we embrace the breakage and perhaps look into other places where this unified error can be used. And maybe morepanic
s can be removed in the codebase.The good thing is that I didn't use any new dependencies. There are 2 best ways to handle errors in the main (in the CLI):
run()
and printing all the errors once in themain()
where therun()
is called;main() -> ExitCode
and returning eitherSUCCESS
/FAILURE
or usingfrom(u8)
which I didn't know of before.main() -> Return<(), ExitCode>
isn't great, because not only you have to mapDebug
impl toDisplay
impl forthiserror
's error messages to work, but also on?
inmain()
you always will getError:
at the end of any error message.I think the code can significantly simplified even further if the secondary
main()
function is used (run()
). For now I created a few helpful macros to easily return the necessary exit code from the main.BTW, I haven't found any table with "error code → error type" mappings in the docs. So different exit codes now look random, but I kept them.
I think that other than
run()
and deciding on the "public API return type change" there isn't anything else I think should be done. Well, OK, there are 2 other things that can be done separately: my initial issue and the renaming of thee.g.
/i.e
(which is like about 7 lines of change).