Skip to content
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

fix(time-style): cryptic panic with invalid --time-syle format string #1282

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

zea64
Copy link
Contributor

@zea64 zea64 commented Dec 29, 2024

Fixes: #1240

Validate --time-style format string during option parsing.

That format string is later given to chrono via DateTime.format(fmt).to_string(). ToString is implemented for T: Display in std but panics if Display::fmt returns an error, and the object returned by DateTime.format (StrftimeItems) lazily returns an error in Display::fmt when it finally sees something invalid in the format string. This is also done inside a rayon threadpool so the resulting panic has little context for users.

The fix is to create a dummy StrftimeItems and iterate over it for errors to validate the format string beforehand.

@zea64 zea64 requested a review from MartinFillon as a code owner December 29, 2024 01:45
Copy link
Member

@cafkafk cafkafk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix is to create a dummy StrftimeItems and iterate over it for errors to validate the format string beforehand.

I wonder what the performance characteristics of doing this is


Currently, snapshot tests are failling, and will have to be regenerated. just has a template in the repository for doing this


I have a few comment here, sorry for the volume of change requests but I do think none of them are nitpicky

let non_recent = if non_recent.is_empty() {
panic!("{}", empty_non_recent_format_msg)
panic!("{}", invalid_non_recent_format_msg)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd prefer exiting with an appropriate exit code, like for instance as how is done here:

                    eprintln!("{e}");
                    trace!("exa.run: exit RUNTIME_ERROR");
                    exit(exits::RUNTIME_ERROR);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems exits isn't pub, so I'd have to change that. Would it be better to just return an error via OptionsError? OptionsError::Unsupported seems like a good candidate.

let empty_non_recent_format_msg = "Custom timestamp format is empty, \
please supply a chrono format string after the plus sign.";
let non_recent = lines.next().expect(empty_non_recent_format_msg);
let invalid_non_recent_format_msg = "Custom timestamp format is invalid, \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be more appropriate as a const, I know it wasn't introduced in this PR but it would make a lot of sense to fix it while here

let non_recent = lines.next().expect(empty_non_recent_format_msg);
let invalid_non_recent_format_msg = "Custom timestamp format is invalid, \
please supply a valid chrono format string after the plus sign.";
let non_recent = lines.next().expect(invalid_non_recent_format_msg);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it would also be more appropriate to handle the error instead of just panicking through expect

Comment on lines 381 to 385
StrftimeItems::new(rec).for_each(|item| {
if matches!(item, Item::Error) {
panic!("{}", invalid_recent_format_msg)
}
});
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should include a comment explaining the motivation behind the code here, as e.g. described in the PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, seeing as this is present above as well, it may be worth deduplicating this

@@ -364,12 +371,18 @@ impl TimeFormat {
// - there is nothing after the first `\n`
// line 2 will be empty when:
// - there exist at least 2 `\n`, and no content between the 1st and 2nd `\n`
let empty_recent_format_msg = "Custom timestamp format for recent files is empty, \
please supply a chrono format string at the second line.";
let invalid_recent_format_msg =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also candidate for becoming a const

Validate --time-style format string during option parsing so we can give
a good error message rather than let rayon handle the panic and give no
context.

Fixes: eza-community#1240
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: 🆕 New
Development

Successfully merging this pull request may close these issues.

bug: panic on invalid time-style format
2 participants