Skip to main content

optionstratlib_core/error/
clock.rs

1//! Target crate (ADR-0001 D6): **core**. Owns `ClockError`.
2//!
3//! A build without the `clock` feature has no wall clock, and everything that
4//! asks for the current time gets this error rather than a made-up instant: a
5//! wrong time would silently price an option wrongly.
6
7use expiration_date::error::ExpirationDateError;
8use thiserror::Error;
9
10/// The current time was asked for and this build cannot provide it.
11///
12/// [`crate::utils::time::now`], [`crate::utils::time::evaluation_time`] and
13/// [`crate::utils::time::resolution_base`] return it as such, and
14/// `TradeError`, `PositionError`, `ChainError`, `StrategyError` and
15/// `TransactionError` have a `Clock` variant for it.
16///
17/// A time to expiry is different. `years_to_expiration` and its siblings return
18/// the `expiration_date` crate's own error, which cannot name this one, so
19/// [`ExpirationDateError::InvalidDateTime`] holds its message, and it arrives as
20/// `OptionsError::ExpirationDate`, `PricingError::Options`, `GreeksError::ExpirationDate`
21/// and so on.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
23#[non_exhaustive]
24pub enum ClockError {
25    /// The crate was built without the `clock` feature, and the operation has no
26    /// explicit time to use instead. A reference datetime stands in for the clock only
27    /// where a time to expiry or an expiry date is measured, not where a new record is
28    /// stamped.
29    #[error(
30        "the wall clock is unavailable: this build has no `clock` feature, so pass the time \
31         explicitly, through a date argument or the `*_at` form of the call where there is \
32         one; a time to expiry can also measure from the thread's reference datetime"
33    )]
34    Unavailable,
35}
36
37impl From<ClockError> for ExpirationDateError {
38    #[cold]
39    #[inline(never)]
40    fn from(err: ClockError) -> Self {
41        Self::InvalidDateTime(err.to_string())
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn test_clock_error_converts_to_invalid_datetime_keeping_its_message() {
51        let message = ClockError::Unavailable.to_string();
52        let converted = ExpirationDateError::from(ClockError::Unavailable);
53        assert!(
54            matches!(converted, ExpirationDateError::InvalidDateTime(ref text) if *text == message)
55        );
56        assert!(message.contains("wall clock is unavailable"));
57    }
58}