From e7799cc1c3efc3f2f16b40791076d6cc9c4f0f17 Mon Sep 17 00:00:00 2001 From: Compey Date: Mon, 4 Sep 2023 12:58:30 +0530 Subject: [PATCH] refactor: proper error handling w/o panics --- src/lune/builtins/datetime/date_time.rs | 85 ++++++++++++++----------- src/lune/builtins/datetime/mod.rs | 18 ++++-- 2 files changed, 59 insertions(+), 44 deletions(-) diff --git a/src/lune/builtins/datetime/date_time.rs b/src/lune/builtins/datetime/date_time.rs index 1360617..7d6bab4 100644 --- a/src/lune/builtins/datetime/date_time.rs +++ b/src/lune/builtins/datetime/date_time.rs @@ -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. /// - 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. - pub fn from_universal_time(date_time: Option) -> Self { + pub fn from_universal_time(date_time: Option) -> Result { if let Some(date_time) = date_time { let utc_time: ChronoDateTime = Utc.from_utc_datetime(&NaiveDateTime::new( - NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day) - .expect("invalid date"), + NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day).ok_or(())?, NaiveTime::from_hms_milli_opt( date_time.hour, date_time.minute, date_time.second, date_time.millisecond, ) - .expect("invalid time"), + .ok_or(())?, )); - Self { + Ok(Self { unix_timestamp: utc_time.timestamp(), unix_timestamp_millis: utc_time.timestamp_millis(), - } + }) } else { let utc_time = Utc::now(); - Self { + Ok(Self { unix_timestamp: utc_time.timestamp(), 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. /// - 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. - pub fn from_local_time(date_time: Option) -> Self { + pub fn from_local_time(date_time: Option) -> Result { if let Some(date_time) = date_time { let local_time: ChronoDateTime = Local .from_local_datetime(&NaiveDateTime::new( NaiveDate::from_ymd_opt(date_time.year, date_time.month, date_time.day) - .expect("invalid date"), + .ok_or(())?, NaiveTime::from_hms_milli_opt( date_time.hour, date_time.minute, date_time.second, date_time.millisecond, ) - .expect("invalid time"), + .ok_or(())?, )) - .unwrap(); + .single() + .ok_or(())?; - Self { + Ok(Self { unix_timestamp: local_time.timestamp(), unix_timestamp_millis: local_time.timestamp_millis(), - } + }) } else { let local_time = Local::now(); - Self { + Ok(Self { unix_timestamp: local_time.timestamp(), 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 /// description. The values within this table could be passed to `from_local_time` /// to produce the original `DateTime` object. - pub fn to_datetime_builder(date_time: ChronoDateTime) -> DateTimeBuilder + pub fn to_datetime_builder(date_time: ChronoDateTime) -> Result where T: TimeZone, { @@ -171,26 +171,26 @@ impl DateTime { date_time_constructor .with_year(date_time.year()) .with_month(match date_time.month() { - 1 => Month::January, - 2 => Month::February, - 3 => Month::March, - 4 => Month::April, - 5 => Month::May, - 6 => Month::June, - 7 => Month::July, - 8 => Month::August, - 9 => Month::September, - 10 => Month::October, - 11 => Month::November, - 12 => Month::December, - _ => panic!("invalid month ordinal"), - }) + 1 => Ok(Month::January), + 2 => Ok(Month::February), + 3 => Ok(Month::March), + 4 => Ok(Month::April), + 5 => Ok(Month::May), + 6 => Ok(Month::June), + 7 => Ok(Month::July), + 8 => Ok(Month::August), + 9 => Ok(Month::September), + 10 => Ok(Month::October), + 11 => Ok(Month::November), + 12 => Ok(Month::December), + _ => Err(()), + }?) .with_day(date_time.day()) .with_hour(date_time.hour()) .with_minute(date_time.minute()) .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 @@ -198,8 +198,13 @@ impl DateTime { /// `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` /// to produce the original `DateTime` object. - pub fn to_local_time(&self) -> DateTimeBuilder { - Self::to_datetime_builder(Local.timestamp_opt(self.unix_timestamp, 0).unwrap()) + pub fn to_local_time(&self) -> Result { + 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). @@ -207,15 +212,19 @@ impl DateTime { /// `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 /// to `from_universal_time` to produce the original `DateTime` object. - pub fn to_universal_time(&self) -> DateTimeBuilder { - Self::to_datetime_builder(Utc.timestamp_opt(self.unix_timestamp, 0).unwrap()) + pub fn to_universal_time(&self) -> Result { + 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. /// The value returned by this function could be passed to `from_local_time` to produce the /// original `DateTime` object. pub fn to_iso_date(&self) -> Result { - self.to_universal_time() + self.to_universal_time()? .to_string::<&str>(Timezone::Utc, None, None) } @@ -234,7 +243,7 @@ impl DateTime { where T: ToString, { - self.to_universal_time().to_string( + self.to_universal_time()?.to_string( timezone, Some(fmt_str.to_string()), Some(locale.to_string()), diff --git a/src/lune/builtins/datetime/mod.rs b/src/lune/builtins/datetime/mod.rs index 9d386ee..4b4391e 100644 --- a/src/lune/builtins/datetime/mod.rs +++ b/src/lune/builtins/datetime/mod.rs @@ -35,10 +35,10 @@ pub fn create(lua: &'static Lua) -> LuaResult { Ok(DateTime::from_unix_timestamp(timestamp_kind, timestamp)) })? .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| { - 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| { Ok(DateTime::from_iso_date(iso_date)) @@ -87,11 +87,15 @@ impl LuaUserData for 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, ()| { - 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 { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult { 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( TimestampType::Seconds, t.get("unixTimestamp")?, )), - _ => panic!("invalid type"), + _ => Err(LuaError::external("invalid type for DateTime self arg")), } } }