From 9c6fe164b82a680caecbe13a31aaddc237824b45 Mon Sep 17 00:00:00 2001
From: Diego Barrios Romero <eldruin@gmail.com>
Date: Sat, 22 May 2021 23:09:10 +0200
Subject: [PATCH] Fix returning error if the device state is invalid and leads
 to invalid date/time values

---
 CHANGELOG.md           |  5 ++++-
 src/ds323x/datetime.rs | 25 +++++++++++--------------
 src/ds323x/mod.rs      | 24 ++++++++++++++++++++++++
 src/lib.rs             |  5 +++++
 4 files changed, 44 insertions(+), 15 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6bc4edd..8b10b70 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,7 +7,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
 
 ## [Unreleased]
 
-...
+### Changed
+- [breaking-change] Return `Error::InvalidDeviceState` if it was not possible to read the
+  date and/or time from the device because the state of the device corresponds to
+  an invalid date and/or time.
 
 ## [0.3.2] - 2021-02-22
 
diff --git a/src/ds323x/datetime.rs b/src/ds323x/datetime.rs
index 2f7a902..acbd79d 100644
--- a/src/ds323x/datetime.rs
+++ b/src/ds323x/datetime.rs
@@ -1,6 +1,8 @@
 //! Common implementation
 
-use super::{decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal};
+use super::{
+    decimal_to_packed_bcd, hours_to_register, packed_bcd_to_decimal, some_or_invalid_error,
+};
 use crate::{
     interface::{ReadData, WriteData},
     BitFlags, Datelike, Ds323x, Error, Hours, NaiveDate, NaiveDateTime, NaiveTime, Register, Rtcc,
@@ -33,11 +35,8 @@ where
         let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
         let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
 
-        Ok(NaiveTime::from_hms(
-            get_h24(hour).into(),
-            minute.into(),
-            second.into(),
-        ))
+        let time = NaiveTime::from_hms_opt(get_h24(hour).into(), minute.into(), second.into());
+        some_or_invalid_error(time)
     }
 
     fn get_weekday(&mut self) -> Result<u8, Self::Error> {
@@ -74,7 +73,8 @@ where
         let month =
             packed_bcd_to_decimal(data[Register::MONTH as usize + 1 - offset] & !BitFlags::CENTURY);
         let day = packed_bcd_to_decimal(data[Register::DOM as usize + 1 - offset]);
-        Ok(NaiveDate::from_ymd(year.into(), month.into(), day.into()))
+        let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
+        some_or_invalid_error(date)
     }
 
     fn get_datetime(&mut self) -> Result<NaiveDateTime, Self::Error> {
@@ -91,13 +91,10 @@ where
         let minute = packed_bcd_to_decimal(data[Register::MINUTES as usize + 1]);
         let second = packed_bcd_to_decimal(data[Register::SECONDS as usize + 1]);
 
-        Ok(
-            rtcc::NaiveDate::from_ymd(year.into(), month.into(), day.into()).and_hms(
-                get_h24(hour).into(),
-                minute.into(),
-                second.into(),
-            ),
-        )
+        let date = NaiveDate::from_ymd_opt(year.into(), month.into(), day.into());
+        let date = some_or_invalid_error(date)?;
+        let datetime = date.and_hms_opt(get_h24(hour).into(), minute.into(), second.into());
+        some_or_invalid_error(datetime)
     }
 
     fn set_seconds(&mut self, seconds: u8) -> Result<(), Self::Error> {
diff --git a/src/ds323x/mod.rs b/src/ds323x/mod.rs
index 1a8ea3c..b0c7729 100644
--- a/src/ds323x/mod.rs
+++ b/src/ds323x/mod.rs
@@ -28,10 +28,34 @@ fn hours_to_register<CommE, PinE>(hours: Hours) -> Result<u8, Error<CommE, PinE>
     }
 }
 
+fn some_or_invalid_error<T, CommE, PinE>(data: Option<T>) -> Result<T, Error<CommE, PinE>> {
+    if let Some(data) = data {
+        Ok(data)
+    } else {
+        Err(Error::InvalidDeviceState)
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
 
+    #[test]
+    fn if_some_then_get_inner() {
+        match some_or_invalid_error::<u8, (), ()>(Some(1)) {
+            Ok(1) => (),
+            _ => panic!(),
+        }
+    }
+
+    #[test]
+    fn if_none_then_error() {
+        match some_or_invalid_error::<u8, (), ()>(None) {
+            Err(Error::InvalidDeviceState) => (),
+            _ => panic!(),
+        }
+    }
+
     #[test]
     fn can_convert_packed_bcd_to_decimal() {
         assert_eq!(0, packed_bcd_to_decimal(0b0000_0000));
diff --git a/src/lib.rs b/src/lib.rs
index 90c61ce..ad2c580 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -408,6 +408,11 @@ pub enum Error<CommE, PinE> {
     Pin(PinE),
     /// Invalid input data provided
     InvalidInputData,
+    /// Internal device state is invalid.
+    ///
+    /// It was not possible to read a valid date and/or time.
+    /// The device is probably missing initialization.
+    InvalidDeviceState,
 }
 
 /// Square-wave output frequency