dtt/error.rs
1// error.rs
2//
3// Copyright © 2025 DateTime (DTT) library. All rights reserved.
4// SPDX-License-Identifier: Apache-2.0 OR MIT
5
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use std::{
9 env,
10 hash::{Hash, Hasher},
11};
12use thiserror::Error;
13use time::error::{ComponentRange, Parse};
14
15/// Custom error type for the application.
16///
17/// This error type encapsulates all possible errors that might occur in the application,
18/// including simulated errors for testing and environment variable retrieval errors.
19#[derive(Error, Debug)]
20pub enum AppError {
21 /// Error that occurs during datetime operations.
22 #[error("DateTime operation error: {0}")]
23 DateTimeError(#[from] DateTimeError),
24
25 /// Error that occurs during serialization.
26 #[cfg(feature = "serde")]
27 #[error("Serialization error: {0}")]
28 SerializationError(#[from] serde_json::Error),
29
30 /// General I/O or parsing error.
31 #[error("General I/O or parsing error: {0}")]
32 GeneralError(#[from] std::io::Error),
33
34 /// Error that occurs during other operations.
35 #[error("Other error: {0}")]
36 Other(String),
37
38 /// Error for simulating a failure in test mode.
39 #[error("Simulated error")]
40 SimulatedError,
41
42 /// Error that occurs when retrieving environment variables.
43 #[error("Environment variable error: {0}")]
44 EnvVarError(#[from] env::VarError),
45}
46
47/// Custom error type for the `DateTime` library.
48///
49/// This enum represents various errors that can occur when working with
50/// `DateTime` objects, such as invalid formats, timezones, and component ranges.
51#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
52pub enum DateTimeError {
53 /// The provided date format is invalid.
54 #[error("Invalid date format")]
55 InvalidFormat,
56
57 /// The provided timezone is invalid or not supported. DST is not supported.
58 #[error("Invalid or unsupported timezone; DST not supported")]
59 InvalidTimezone,
60
61 /// The date is invalid (e.g., February 30).
62 #[error("Invalid date")]
63 InvalidDate,
64
65 /// The time is invalid (e.g., 25:00).
66 #[error("Invalid time")]
67 InvalidTime,
68
69 /// An error occurred while parsing the date/time string.
70 #[error("Parsing error")]
71 ParseError(#[from] Parse),
72
73 /// A component (year, month, day, etc.) is out of the valid range.
74 #[error("Component range error")]
75 ComponentRange(#[from] ComponentRange),
76}
77
78impl Hash for DateTimeError {
79 /// Custom implementation of the `Hash` trait for `DateTimeError`.
80 ///
81 /// This allows `DateTimeError` to be used in hashed collections like `HashSet` and `HashMap`.
82 fn hash<H: Hasher>(&self, state: &mut H) {
83 // Use the discriminant of the enum as a simple hash value
84 std::mem::discriminant(self).hash(state);
85 }
86}
87
88#[cfg(feature = "serde")]
89impl Serialize for DateTimeError {
90 /// Serializes the `DateTimeError` into a string representation.
91 ///
92 /// This is a custom implementation to handle serialization for variants
93 /// that contain types (`Parse` and `ComponentRange`) which do not implement
94 /// `Serialize`.
95 ///
96 /// # Errors
97 ///
98 /// This function will return a serialization error if the process fails.
99 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100 where
101 S: Serializer,
102 {
103 match self {
104 Self::InvalidFormat => {
105 serializer.serialize_str("InvalidFormat")
106 }
107 Self::InvalidTimezone => {
108 serializer.serialize_str("InvalidTimezone")
109 }
110 Self::InvalidDate => {
111 serializer.serialize_str("InvalidDate")
112 }
113 Self::InvalidTime => {
114 serializer.serialize_str("InvalidTime")
115 }
116 Self::ParseError(_) => {
117 serializer.serialize_str("ParseError")
118 }
119 Self::ComponentRange(_) => {
120 serializer.serialize_str("ComponentRange")
121 }
122 }
123 }
124}
125
126#[cfg(feature = "serde")]
127impl<'de> Deserialize<'de> for DateTimeError {
128 /// Deserializes a string into a `DateTimeError`.
129 ///
130 /// This is a custom implementation to handle deserialization for variants
131 /// that contain types (`Parse` and `ComponentRange`) which do not implement
132 /// `Deserialize`.
133 ///
134 /// # Errors
135 ///
136 /// This function will return a deserialization error if the input string
137 /// does not match any of the known variants.
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 let s: &str = Deserialize::deserialize(deserializer)?;
143 match s {
144 "InvalidFormat" => Ok(Self::InvalidFormat),
145 "InvalidTimezone" => Ok(Self::InvalidTimezone),
146 "InvalidDate" => Ok(Self::InvalidDate),
147 "InvalidTime" => Ok(Self::InvalidTime),
148 "ParseError" => Err(serde::de::Error::custom(
149 "Cannot deserialize ParseError directly",
150 )),
151 "ComponentRange" => Err(serde::de::Error::custom(
152 "Cannot deserialize ComponentRange directly",
153 )),
154 _ => Err(serde::de::Error::unknown_variant(
155 s,
156 &[
157 "InvalidFormat",
158 "InvalidTimezone",
159 "InvalidDate",
160 "InvalidTime",
161 "ParseError",
162 "ComponentRange",
163 ],
164 )),
165 }
166 }
167}
168
169impl Default for DateTimeError {
170 /// Provides a default value for `DateTimeError`.
171 ///
172 /// By default, the error is set to `InvalidFormat`.
173 ///
174 /// # Examples
175 ///
176 /// ```
177 /// use dtt::error::DateTimeError;
178 ///
179 /// let error = DateTimeError::default();
180 /// assert_eq!(error, DateTimeError::InvalidFormat);
181 /// ```
182 fn default() -> Self {
183 Self::InvalidFormat
184 }
185}