refactor: proper error handling w/o panics

This commit is contained in:
Erica Marigold 2023-09-04 12:58:30 +05:30
parent d9eca85ac2
commit e7799cc1c3
No known key found for this signature in database
GPG key ID: 23CD97ABBBCC5ED2
2 changed files with 59 additions and 44 deletions

View file

@ -66,31 +66,30 @@ impl DateTime {
/// - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1. /// - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1.
/// - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes. /// - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes.
/// - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970. /// - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970.
pub fn from_universal_time(date_time: Option<DateTimeBuilder>) -> Self { pub fn from_universal_time(date_time: Option<DateTimeBuilder>) -> Result<Self, ()> {
if let Some(date_time) = date_time { if let Some(date_time) = date_time {
let utc_time: ChronoDateTime<Utc> = Utc.from_utc_datetime(&NaiveDateTime::new( let utc_time: ChronoDateTime<Utc> = Utc.from_utc_datetime(&NaiveDateTime::new(
NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day) NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day).ok_or(())?,
.expect("invalid date"),
NaiveTime::from_hms_milli_opt( NaiveTime::from_hms_milli_opt(
date_time.hour, date_time.hour,
date_time.minute, date_time.minute,
date_time.second, date_time.second,
date_time.millisecond, date_time.millisecond,
) )
.expect("invalid time"), .ok_or(())?,
)); ));
Self { Ok(Self {
unix_timestamp: utc_time.timestamp(), unix_timestamp: utc_time.timestamp(),
unix_timestamp_millis: utc_time.timestamp_millis(), unix_timestamp_millis: utc_time.timestamp_millis(),
} })
} else { } else {
let utc_time = Utc::now(); let utc_time = Utc::now();
Self { Ok(Self {
unix_timestamp: utc_time.timestamp(), unix_timestamp: utc_time.timestamp(),
unix_timestamp_millis: utc_time.timestamp_millis(), unix_timestamp_millis: utc_time.timestamp_millis(),
} })
} }
} }
@ -102,33 +101,34 @@ impl DateTime {
/// - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1. /// - Time units (hour, minute, second, millisecond) that are outside their normal range are valid. For example, 90 minutes will cause the hour to roll over by 1; -10 seconds will cause the minute value to roll back by 1.
/// - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes. /// - Non-integer values are rounded down. For example, providing 2.5 hours will be equivalent to providing 2 hours, not 2 hours 30 minutes.
/// - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970. /// - Omitted values are assumed to be their lowest value in their normal range, except for year which defaults to 1970.
pub fn from_local_time(date_time: Option<DateTimeBuilder>) -> Self { pub fn from_local_time(date_time: Option<DateTimeBuilder>) -> Result<Self, ()> {
if let Some(date_time) = date_time { if let Some(date_time) = date_time {
let local_time: ChronoDateTime<Local> = Local let local_time: ChronoDateTime<Local> = Local
.from_local_datetime(&NaiveDateTime::new( .from_local_datetime(&NaiveDateTime::new(
NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day) NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day)
.expect("invalid date"), .ok_or(())?,
NaiveTime::from_hms_milli_opt( NaiveTime::from_hms_milli_opt(
date_time.hour, date_time.hour,
date_time.minute, date_time.minute,
date_time.second, date_time.second,
date_time.millisecond, date_time.millisecond,
) )
.expect("invalid time"), .ok_or(())?,
)) ))
.unwrap(); .single()
.ok_or(())?;
Self { Ok(Self {
unix_timestamp: local_time.timestamp(), unix_timestamp: local_time.timestamp(),
unix_timestamp_millis: local_time.timestamp_millis(), unix_timestamp_millis: local_time.timestamp_millis(),
} })
} else { } else {
let local_time = Local::now(); let local_time = Local::now();
Self { Ok(Self {
unix_timestamp: local_time.timestamp(), unix_timestamp: local_time.timestamp(),
unix_timestamp_millis: local_time.timestamp_millis(), unix_timestamp_millis: local_time.timestamp_millis(),
} })
} }
} }
@ -159,7 +159,7 @@ impl DateTime {
/// `Millisecond`. For more details, see the time value table in this data type's /// `Millisecond`. For more details, see the time value table in this data type's
/// description. The values within this table could be passed to `from_local_time` /// description. The values within this table could be passed to `from_local_time`
/// to produce the original `DateTime` object. /// to produce the original `DateTime` object.
pub fn to_datetime_builder<T>(date_time: ChronoDateTime<T>) -> DateTimeBuilder pub fn to_datetime_builder<T>(date_time: ChronoDateTime<T>) -> Result<DateTimeBuilder, ()>
where where
T: TimeZone, T: TimeZone,
{ {
@ -171,26 +171,26 @@ impl DateTime {
date_time_constructor date_time_constructor
.with_year(date_time.year()) .with_year(date_time.year())
.with_month(match date_time.month() { .with_month(match date_time.month() {
1 => Month::January, 1 => Ok(Month::January),
2 => Month::February, 2 => Ok(Month::February),
3 => Month::March, 3 => Ok(Month::March),
4 => Month::April, 4 => Ok(Month::April),
5 => Month::May, 5 => Ok(Month::May),
6 => Month::June, 6 => Ok(Month::June),
7 => Month::July, 7 => Ok(Month::July),
8 => Month::August, 8 => Ok(Month::August),
9 => Month::September, 9 => Ok(Month::September),
10 => Month::October, 10 => Ok(Month::October),
11 => Month::November, 11 => Ok(Month::November),
12 => Month::December, 12 => Ok(Month::December),
_ => panic!("invalid month ordinal"), _ => Err(()),
}) }?)
.with_day(date_time.day()) .with_day(date_time.day())
.with_hour(date_time.hour()) .with_hour(date_time.hour())
.with_minute(date_time.minute()) .with_minute(date_time.minute())
.with_second(date_time.second()); .with_second(date_time.second());
date_time_constructor Ok(date_time_constructor)
} }
/// Converts the value of this `DateTime` object to local time. The returned table /// Converts the value of this `DateTime` object to local time. The returned table
@ -198,8 +198,13 @@ impl DateTime {
/// `Millisecond`. For more details, see the time value table in this data type's /// `Millisecond`. For more details, see the time value table in this data type's
/// description. The values within this table could be passed to `from_local_time` /// description. The values within this table could be passed to `from_local_time`
/// to produce the original `DateTime` object. /// to produce the original `DateTime` object.
pub fn to_local_time(&self) -> DateTimeBuilder { pub fn to_local_time(&self) -> Result<DateTimeBuilder, ()> {
Self::to_datetime_builder(Local.timestamp_opt(self.unix_timestamp, 0).unwrap()) Ok(Self::to_datetime_builder(
Local
.timestamp_opt(self.unix_timestamp, 0)
.single()
.ok_or(())?,
)?)
} }
/// Converts the value of this `DateTime` object to Universal Coordinated Time (UTC). /// Converts the value of this `DateTime` object to Universal Coordinated Time (UTC).
@ -207,15 +212,19 @@ impl DateTime {
/// `Minute`, `Second`, `Millisecond`. For more details, see the time value table /// `Minute`, `Second`, `Millisecond`. For more details, see the time value table
/// in this data type's description. The values within this table could be passed /// in this data type's description. The values within this table could be passed
/// to `from_universal_time` to produce the original `DateTime` object. /// to `from_universal_time` to produce the original `DateTime` object.
pub fn to_universal_time(&self) -> DateTimeBuilder { pub fn to_universal_time(&self) -> Result<DateTimeBuilder, ()> {
Self::to_datetime_builder(Utc.timestamp_opt(self.unix_timestamp, 0).unwrap()) Ok(Self::to_datetime_builder(
Utc.timestamp_opt(self.unix_timestamp, 0)
.single()
.ok_or(())?,
)?)
} }
/// Formats a date as a ISO 8601 date-time string, returns None if the DateTime object is invalid. /// Formats a date as a ISO 8601 date-time string, returns None if the DateTime object is invalid.
/// The value returned by this function could be passed to `from_local_time` to produce the /// The value returned by this function could be passed to `from_local_time` to produce the
/// original `DateTime` object. /// original `DateTime` object.
pub fn to_iso_date(&self) -> Result<String, ()> { pub fn to_iso_date(&self) -> Result<String, ()> {
self.to_universal_time() self.to_universal_time()?
.to_string::<&str>(Timezone::Utc, None, None) .to_string::<&str>(Timezone::Utc, None, None)
} }
@ -234,7 +243,7 @@ impl DateTime {
where where
T: ToString, T: ToString,
{ {
self.to_universal_time().to_string( self.to_universal_time()?.to_string(
timezone, timezone,
Some(fmt_str.to_string()), Some(fmt_str.to_string()),
Some(locale.to_string()), Some(locale.to_string()),

View file

@ -35,10 +35,10 @@ pub fn create(lua: &'static Lua) -> LuaResult<LuaTable> {
Ok(DateTime::from_unix_timestamp(timestamp_kind, timestamp)) Ok(DateTime::from_unix_timestamp(timestamp_kind, timestamp))
})? })?
.with_function("fromUniversalTime", |lua, date_time: LuaValue| { .with_function("fromUniversalTime", |lua, date_time: LuaValue| {
Ok(DateTime::from_universal_time(DateTimeBuilder::from_lua(date_time, lua).ok())) Ok(DateTime::from_universal_time(DateTimeBuilder::from_lua(date_time, lua).ok()).or(Err(LuaError::external("invalid DateTimeValues provided to fromUniversalTime"))))
})? })?
.with_function("fromLocalTime", |lua, date_time: LuaValue| { .with_function("fromLocalTime", |lua, date_time: LuaValue| {
Ok(DateTime::from_local_time(DateTimeBuilder::from_lua(date_time, lua).ok())) Ok(DateTime::from_local_time(DateTimeBuilder::from_lua(date_time, lua).ok()).or(Err(LuaError::external("invalid DateTimeValues provided to fromLocalTime"))))
})? })?
.with_function("fromIsoDate", |_, iso_date: String| { .with_function("fromIsoDate", |_, iso_date: String| {
Ok(DateTime::from_iso_date(iso_date)) Ok(DateTime::from_iso_date(iso_date))
@ -87,11 +87,15 @@ impl LuaUserData for DateTime {
); );
methods.add_method("toUniversalTime", |_, this: &DateTime, ()| { methods.add_method("toUniversalTime", |_, this: &DateTime, ()| {
Ok(this.to_universal_time()) Ok(this.to_universal_time().or(Err(LuaError::external(
"invalid DateTime self argument provided to toUniversalTime",
))))
}); });
methods.add_method("toLocalTime", |_, this: &DateTime, ()| { methods.add_method("toLocalTime", |_, this: &DateTime, ()| {
Ok(this.to_local_time()) Ok(this.to_local_time().or(Err(LuaError::external(
"invalid DateTime self argument provided to toLocalTime",
))))
}); });
} }
} }
@ -99,12 +103,14 @@ impl LuaUserData for DateTime {
impl<'lua> FromLua<'lua> for DateTime { impl<'lua> FromLua<'lua> for DateTime {
fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<Self> {
match value { match value {
LuaValue::Nil => panic!("found nil"), LuaValue::Nil => Err(LuaError::external(
"expected self of type DateTime, found nil",
)),
LuaValue::Table(t) => Ok(DateTime::from_unix_timestamp( LuaValue::Table(t) => Ok(DateTime::from_unix_timestamp(
TimestampType::Seconds, TimestampType::Seconds,
t.get("unixTimestamp")?, t.get("unixTimestamp")?,
)), )),
_ => panic!("invalid type"), _ => Err(LuaError::external("invalid type for DateTime self arg")),
} }
} }
} }