-
Notifications
You must be signed in to change notification settings - Fork 542
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 StrftimeItems::new return Result #902
Make StrftimeItems::new return Result #902
Conversation
a8fe63d
to
f3ec848
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.
This is great, thanks for fixing this up!
ffdcb46
to
25e8396
Compare
Ok, I have things mostly working except for some of the configurations. I'm not sure how best to handle the failures though. |
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, this is looking great!
src/format/strftime.rs
Outdated
@@ -184,11 +186,6 @@ use alloc::vec::Vec; | |||
use super::{locales, Locale}; | |||
use super::{Fixed, InternalFixed, InternalInternal, Item, Numeric, Pad}; | |||
|
|||
#[cfg(feature = "unstable-locales")] |
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 is the cause of the test failures. Note that, without unstable-locales
, this is a slice, not a Vec
. chrono without default features (and in WASM scenarios) has included some formatting functionality in that case, but Vec
is not available there. So we have to find a way to use a &[Item<'a>]
in StrftimeItems
.
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 could be a problem. I'm assuming that other collection types like LinkedList
are also unavailable, and I don't see a good way to do this without a resizeable collection.
We could go back to the system where the iterator generates elements as we go and just run through the string once at creation time to check it's all valid. That feels wasteful though.
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.
Having spent some time thinking about it, I think we do want to have StrftimeItems::new
return a Result
but I can't see any way to use an array as we can't give a size at compile time.
So my current plan is to go back to the system of storing the format string as a str
and processing it as the iterator is called but iterate through the format string once at creation time to check for problems. Do you have any objections or better ideas?
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 try to look at it again tomorrow.
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.
No better ideas, so let's do it that way for now.
let d_fmt = StrftimeItems::new(locales::d_fmt(locale)).collect(); | ||
let d_t_fmt = StrftimeItems::new(locales::d_t_fmt(locale)).collect(); | ||
let t_fmt = StrftimeItems::new(locales::t_fmt(locale)).collect(); | ||
pub fn new(s: &'a str) -> Result<StrftimeItems<'a>, ParseError> { |
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.
Perhaps we should have a new function parse
or similar for this, and keep the existing function as a lazy iterator? My thinking for this is that if we just want to validate strftime strings, the iterator is more efficient as we can bail out earlier if we hit an error. Alternatively, we may want to collect all the errors rather than the first one
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 use case for just validating a strftime string? I also think it's pretty unusual for things to collect all errors instead of bailing on the first one (for example, the FromIterator
impl for Result
).
db8c8e7
to
fac117e
Compare
can someone explain the failures? The error message seems to be pointing to a line that doesn't exist in the source. |
If anybody needs a workaround until this is merged, here's what I cooked up: // Workaround to avoid panicking when the user-provided format string is invalid.
// Will be obsolete once https://github.com/chronotope/chrono/pull/902 is merged.
std::panic::set_hook(Box::new(|_| ()));
let formatted = std::panic::catch_unwind(|| {
datetime.format(&description).to_string()
})?;
let _ = std::panic::take_hook(); A dummy panic hook is necessary to suppress the panic message. I don't think this actually incurs an allocation ( (Paging #956 as well.) |
@SomewhereOutInSpace - you can also use a workaround as mentioned here #614 (comment). The main points are using |
@jaggededgedjustice - the errors look mainly due to cases where what was previously a |
@esheppa the bit I'm struggling with is finding the line that's throwing the error. Consider, https://github.com/chronotope/chrono/actions/runs/3980518900/jobs/6823655339#logs which says
The error makes sense for the line shown, but I can't find where that line is. The error message points to and a search shows no call to
So I have no idea what's going on, unless maybe the worker that ran the last build had an old copy of some code. |
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 needs additional test cases for the path where a ParseError
is returned.
fac117e
to
83fe746
Compare
Do you want a test for every function that can now return a |
0f6896e
to
97c48e9
Compare
If you would please, I'd really appreciate it. While Hopefully @djc and @esheppa agree. Final decisions should defer to them. |
db62b24
to
42a4abd
Compare
@jtmoon79 that does seem reasonable overall, but as far as I can see there aren't any new (non testing) private functions in this PR? However I think it would be nice to have some basic tests on the |
Good point. I had too many PR reviews in my head when I wrote that 🥴 |
Are you happy with the current tests that rely on |
|
||
#[test] | ||
fn test_format() { | ||
assert!( |
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 rename to fn test_date_format_err()
and add a few more tests within it.
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.
Suggestions:
""
(not sure if this should error, you'll have to try it)"%X"
"%%%"
"%Y%"
" %Y% "
"%ぁ"
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.
How many test cases do think would be required? Personally I'd leave it with just the 1 case as the error is just being passed through from StrftimeItems::new()
which has test_strftime_items
to check a wide range of inputs so I don't see the benefit of duplicating the check here.
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.
Yeah, that's reasonable.
src/naive/datetime/mod.rs
Outdated
@@ -1921,3 +1921,16 @@ where | |||
assert!(from_str(r#"{"date":{"ymdf":20},"time":{"secs":0,"frac":0}}"#).is_err()); | |||
assert!(from_str(r#"null"#).is_err()); | |||
} | |||
|
|||
#[test] | |||
fn test_format() { |
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 rename to fn test_datetime_format_err()
and add a few more tests within it.
Suggestions:
""
(not sure if this should error, you'll have to try it)"%%%"
"%Y%"
" %Y% "
"%ぁ"
42a4abd
to
72a598d
Compare
|
||
#[test] | ||
fn test_format() { | ||
assert!( |
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.
Yeah, that's reasonable.
src/naive/time/mod.rs
Outdated
@@ -1381,3 +1381,11 @@ where | |||
assert!(from_str(r#"{"secs":0,"frac":0}"#).is_err()); | |||
assert!(from_str(r#"null"#).is_err()); | |||
} | |||
|
|||
#[test] | |||
fn test_format() { |
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 rename this to test_time_format_err
.
a39ca78
to
82df95c
Compare
82df95c
to
d0dc66f
Compare
Edit: (I have to think a bit more before writing this comment) |
While I agree this issue is worth fixing, I am not sure the solution here is the best. My concern are the ergonomics for library users. Also this solution requires parsing the format string twice, which is unfortunate. If I understand it right, the only combination a panic is designed to happen with the current formatting and parsing code is in this snippet: let date = NaiveDate::from_ymd_opt(2023, 5, 31).unwrap();
let date_str = date.format("%Y-%M-%D %").to_string(); The actual panic doesn't happen in any code that is part of chrono. It happens in If you use let date = NaiveDate::from_ymd_opt(2023, 5, 31).unwrap();
let date_str = format!("{}", date.format("%Y-%M-%D %"))?;
/// or:
println!("{}", date.format("%Y-%M-%D %"))?;
As an alternative (that also works on 0.4.x): what if we had convenience methods Also there are two cases that are currently not covered by the solution in this PR:
|
Found some interesting history. In 2017 The documentation for
So that the |
My new thoughts on this PR (I hope I now finally have all the context...)
|
I feel like this ends up being a lot of pain while not getting us to the API we really want. Please have a look at #1127 and give feedback on the proposed design. |
While only a first step towards a proper solution, we did get Closing this PR. @jaggededgedjustice thanks for working on this though! |
This makes the
date_time.format()
function return aResult
instead of panicking when passed an invalid format string. If any part of the format string is invalid an error is returned.This is a breaking change for the api.
Fixes #623
closes #614