Skip to content

Commit ab09c47

Browse files
committed
Provide details when displaying component errors
1 parent 7528763 commit ab09c47

File tree

5 files changed

+95
-62
lines changed

5 files changed

+95
-62
lines changed

src/date.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -990,13 +990,17 @@ impl Date {
990990

991991
// Verification for all components is done at parse time.
992992
match items {
993-
items!(year, month, day) => Ok(internals::Date::from_ymd_unchecked(year, month.get(), day.get())),
994-
items!(year, ordinal_day) => Ok(internals::Date::from_yo_unchecked(year, ordinal_day.get())),
995-
items!(week_based_year, iso_week, weekday) => Ok(internals::Date::from_iso_ywd_unchecked(
996-
week_based_year,
997-
iso_week.get(),
998-
weekday,
993+
items!(year, month, day) => Ok(internals::Date::from_ymd_unchecked(
994+
year,
995+
month.get(),
996+
day.get(),
999997
)),
998+
items!(year, ordinal_day) => {
999+
Ok(internals::Date::from_yo_unchecked(year, ordinal_day.get()))
1000+
}
1001+
items!(week_based_year, iso_week, weekday) => Ok(
1002+
internals::Date::from_iso_ywd_unchecked(week_based_year, iso_week.get(), weekday),
1003+
),
10001004
items!(year, sunday_week, weekday) => Ok(internals::Date::from_yo_unchecked(
10011005
year,
10021006
#[allow(clippy::cast_sign_loss)]

src/error.rs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
use crate::format::ParseError;
2+
#[cfg(not(feature = "std"))]
3+
use crate::no_std_prelude::*;
24
use core::fmt;
35

46
/// A unified error type for anything returned by a method in the time crate.
57
///
68
/// This can be used when you either don't know or don't care about the exact
79
/// error returned. `Result<_, time::Error>` will work in these situations.
10+
// Boxing the `ComponentRangeError` reduces the size of `Error` from 72 bytes to
11+
// 16.
812
#[allow(clippy::missing_docs_in_private_items)] // variants only
913
#[non_exhaustive]
1014
#[derive(Debug, Clone, PartialEq, Eq)]
1115
pub enum Error {
1216
ConversionRange(ConversionRangeError),
13-
ComponentRange(ComponentRangeError),
17+
ComponentRange(Box<ComponentRangeError>),
1418
Parse(ParseError),
1519
}
1620

@@ -53,22 +57,49 @@ impl From<ConversionRangeError> for Error {
5357

5458
/// An error type indicating that a component provided to a method was out of
5559
/// range, causing a failure.
60+
// i64 is the narrowest type fitting all use cases. This eliminates the need
61+
// for a type parameter.
5662
#[allow(missing_copy_implementations)] // Non-copy fields may be added.
5763
#[non_exhaustive]
5864
#[derive(Debug, Clone, PartialEq, Eq)]
59-
pub struct ComponentRangeError;
65+
pub struct ComponentRangeError {
66+
/// Name of the component.
67+
pub(crate) name: &'static str,
68+
/// Minimum allowed value, inclusive.
69+
pub(crate) minimum: i64,
70+
/// Maximum allowed value, inclusive.
71+
pub(crate) maximum: i64,
72+
/// Value that was provided.
73+
pub(crate) value: i64,
74+
/// The minimum and/or maximum is only valid with the following values.
75+
pub(crate) given: Vec<(&'static str, i64)>,
76+
}
6077

6178
impl fmt::Display for ComponentRangeError {
6279
#[inline(always)]
6380
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64-
f.write_str("A component's value is out of range")
81+
write!(
82+
f,
83+
"{} must be in the range {}..={}",
84+
self.name, self.minimum, self.maximum
85+
)?;
86+
87+
let mut iter = self.given.iter();
88+
if let Some((name, value)) = iter.next() {
89+
write!(f, " given {}={}", name, value)?;
90+
for (name, value) in iter {
91+
write!(f, ", {}={}", name, value)?;
92+
}
93+
}
94+
95+
write!(f, " (was {})", self.value)
6596
}
6697
}
6798

6899
impl From<ComponentRangeError> for Error {
69100
#[inline(always)]
70101
fn from(original: ComponentRangeError) -> Self {
71-
Self::ComponentRange(original)
102+
Self::ComponentRange(Box::new(original))
72103
}
73104
}
74105

src/internals.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,10 @@ impl Date {
6464
#[inline]
6565
pub(crate) fn from_iso_ywd_unchecked(year: i32, week: u8, weekday: Weekday) -> crate::Date {
6666
let ordinal = week as u16 * 7 + weekday.iso_weekday_number() as u16
67-
- (Self::from_yo_unchecked(year, 4).weekday().iso_weekday_number() as u16 + 3);
67+
- (Self::from_yo_unchecked(year, 4)
68+
.weekday()
69+
.iso_weekday_number() as u16
70+
+ 3);
6871

6972
if ordinal < 1 {
7073
return Self::from_yo_unchecked(year - 1, ordinal + days_in_year(year - 1));

src/lib.rs

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@
139139
)]
140140
#![warn(
141141
unused_extern_crates,
142-
box_pointers,
143142
missing_copy_implementations,
144143
missing_debug_implementations,
145144
single_use_lifetimes,
@@ -200,17 +199,6 @@ macro_rules! assert_value_in_range {
200199
}
201200
};
202201

203-
($value:ident in $start:expr => exclusive $end:expr) => {
204-
if !($start..$end).contains(&$value) {
205-
panic!(
206-
concat!(stringify!($value), " must be in the range {}..{} (was {})"),
207-
$start,
208-
$end,
209-
$value,
210-
);
211-
}
212-
};
213-
214202
($value:ident in $start:expr => $end:expr, given $($conditional:ident),+ $(,)?) => {
215203
if !($start..=$end).contains(&$value) {
216204
panic!(
@@ -224,23 +212,30 @@ macro_rules! assert_value_in_range {
224212
};
225213
}
226214

215+
// TODO Some of the formatting can likely be performed at compile-time.
227216
/// Returns `None` if the value is not in range.
228217
macro_rules! ensure_value_in_range {
229218
($value:ident in $start:expr => $end:expr) => {
230219
if !($start..=$end).contains(&$value) {
231-
return Err(ComponentRangeError);
232-
}
233-
};
234-
235-
($value:ident in $start:expr => exclusive $end:expr) => {
236-
if !($start..$end).contains(&$value) {
237-
return Err(ComponentRangeError);
220+
return Err(ComponentRangeError {
221+
name: stringify!($value),
222+
minimum: i64::from($start),
223+
maximum: i64::from($end),
224+
value: i64::from($value),
225+
given: Vec::new(),
226+
});
238227
}
239228
};
240229

241-
($value:ident in $start:expr => $end:expr,given $($conditional:ident),+ $(,)?) => {
230+
($value:ident in $start:expr => $end:expr, given $($conditional:ident),+ $(,)?) => {
242231
if !($start..=$end).contains(&$value) {
243-
return Err(ComponentRangeError);
232+
return Err(ComponentRangeError {
233+
name: stringify!($value),
234+
minimum: i64::from($start),
235+
maximum: i64::from($end),
236+
value: i64::from($value),
237+
given: vec![$((stringify!($conditional), i64::from($conditional))),+],
238+
});
244239
};
245240
};
246241
}

src/time.rs

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ impl Time {
8888
#[cfg(feature = "panicking-api")]
8989
#[cfg_attr(doc, doc(cfg(feature = "panicking-api")))]
9090
pub fn from_hms(hour: u8, minute: u8, second: u8) -> Self {
91-
assert_value_in_range!(hour in 0 => exclusive 24);
92-
assert_value_in_range!(minute in 0 => exclusive 60);
93-
assert_value_in_range!(second in 0 => exclusive 60);
91+
assert_value_in_range!(hour in 0 => 23);
92+
assert_value_in_range!(minute in 0 => 59);
93+
assert_value_in_range!(second in 0 => 59);
9494
Self {
9595
hour,
9696
minute,
@@ -116,9 +116,9 @@ impl Time {
116116
/// ```
117117
#[inline(always)]
118118
pub fn try_from_hms(hour: u8, minute: u8, second: u8) -> Result<Self, ComponentRangeError> {
119-
ensure_value_in_range!(hour in 0 => exclusive 24);
120-
ensure_value_in_range!(minute in 0 => exclusive 60);
121-
ensure_value_in_range!(second in 0 => exclusive 60);
119+
ensure_value_in_range!(hour in 0 => 23);
120+
ensure_value_in_range!(minute in 0 => 59);
121+
ensure_value_in_range!(second in 0 => 59);
122122
Ok(Self {
123123
hour,
124124
minute,
@@ -164,10 +164,10 @@ impl Time {
164164
#[cfg(feature = "panicking-api")]
165165
#[cfg_attr(doc, doc(cfg(feature = "panicking-api")))]
166166
pub fn from_hms_milli(hour: u8, minute: u8, second: u8, millisecond: u16) -> Self {
167-
assert_value_in_range!(hour in 0 => exclusive 24);
168-
assert_value_in_range!(minute in 0 => exclusive 60);
169-
assert_value_in_range!(second in 0 => exclusive 60);
170-
assert_value_in_range!(millisecond in 0 => exclusive 1_000);
167+
assert_value_in_range!(hour in 0 => 23);
168+
assert_value_in_range!(minute in 0 => 59);
169+
assert_value_in_range!(second in 0 => 59);
170+
assert_value_in_range!(millisecond in 0 => 999);
171171
Self {
172172
hour,
173173
minute,
@@ -199,10 +199,10 @@ impl Time {
199199
second: u8,
200200
millisecond: u16,
201201
) -> Result<Self, ComponentRangeError> {
202-
ensure_value_in_range!(hour in 0 => exclusive 24);
203-
ensure_value_in_range!(minute in 0 => exclusive 60);
204-
ensure_value_in_range!(second in 0 => exclusive 60);
205-
ensure_value_in_range!(millisecond in 0 => exclusive 1_000);
202+
ensure_value_in_range!(hour in 0 => 23);
203+
ensure_value_in_range!(minute in 0 => 59);
204+
ensure_value_in_range!(second in 0 => 59);
205+
ensure_value_in_range!(millisecond in 0 => 999);
206206
Ok(Self {
207207
hour,
208208
minute,
@@ -248,10 +248,10 @@ impl Time {
248248
#[cfg(feature = "panicking-api")]
249249
#[cfg_attr(doc, doc(cfg(feature = "panicking-api")))]
250250
pub fn from_hms_micro(hour: u8, minute: u8, second: u8, microsecond: u32) -> Self {
251-
assert_value_in_range!(hour in 0 => exclusive 24);
252-
assert_value_in_range!(minute in 0 => exclusive 60);
253-
assert_value_in_range!(second in 0 => exclusive 60);
254-
assert_value_in_range!(microsecond in 0 => exclusive 1_000_000);
251+
assert_value_in_range!(hour in 0 => 23);
252+
assert_value_in_range!(minute in 0 => 59);
253+
assert_value_in_range!(second in 0 => 59);
254+
assert_value_in_range!(microsecond in 0 => 999_999);
255255
Self {
256256
hour,
257257
minute,
@@ -283,10 +283,10 @@ impl Time {
283283
second: u8,
284284
microsecond: u32,
285285
) -> Result<Self, ComponentRangeError> {
286-
ensure_value_in_range!(hour in 0 => exclusive 24);
287-
ensure_value_in_range!(minute in 0 => exclusive 60);
288-
ensure_value_in_range!(second in 0 => exclusive 60);
289-
ensure_value_in_range!(microsecond in 0 => exclusive 1_000_000);
286+
ensure_value_in_range!(hour in 0 => 23);
287+
ensure_value_in_range!(minute in 0 => 59);
288+
ensure_value_in_range!(second in 0 => 59);
289+
ensure_value_in_range!(microsecond in 0 => 999_999);
290290
Ok(Self {
291291
hour,
292292
minute,
@@ -331,10 +331,10 @@ impl Time {
331331
#[cfg(feature = "panicking-api")]
332332
#[cfg_attr(doc, doc(cfg(feature = "panicking-api")))]
333333
pub fn from_hms_nano(hour: u8, minute: u8, second: u8, nanosecond: u32) -> Self {
334-
assert_value_in_range!(hour in 0 => exclusive 24);
335-
assert_value_in_range!(minute in 0 => exclusive 60);
336-
assert_value_in_range!(second in 0 => exclusive 60);
337-
assert_value_in_range!(nanosecond in 0 => exclusive 1_000_000_000);
334+
assert_value_in_range!(hour in 0 => 23);
335+
assert_value_in_range!(minute in 0 => 59);
336+
assert_value_in_range!(second in 0 => 59);
337+
assert_value_in_range!(nanosecond in 0 => 999_999_999);
338338
Self {
339339
hour,
340340
minute,
@@ -366,10 +366,10 @@ impl Time {
366366
second: u8,
367367
nanosecond: u32,
368368
) -> Result<Self, ComponentRangeError> {
369-
ensure_value_in_range!(hour in 0 => exclusive 24);
370-
ensure_value_in_range!(minute in 0 => exclusive 60);
371-
ensure_value_in_range!(second in 0 => exclusive 60);
372-
ensure_value_in_range!(nanosecond in 0 => exclusive 1_000_000_000);
369+
ensure_value_in_range!(hour in 0 => 23);
370+
ensure_value_in_range!(minute in 0 => 59);
371+
ensure_value_in_range!(second in 0 => 59);
372+
ensure_value_in_range!(nanosecond in 0 => 999_999_999);
373373
Ok(Self {
374374
hour,
375375
minute,

0 commit comments

Comments
 (0)