Skip to main content

audio_clock_bsd/
error.rs

1//! Crate error types and the [`Result`] alias.
2
3use std::io;
4
5use audio_core_bsd::AudioError;
6
7/// Errors returned by clock-source operations.
8///
9/// These cover the two concerns of this crate:
10///
11/// - **Clock availability / sync** — whether a usable time reference exists at
12///   all (PTP daemon reachable, NTP synced). Represented by [`ClockError::Unavailable`]
13///   and [`ClockError::NotSynced`].
14/// - **Conversion integrity** — sample-rate validation and the checked
15///   arithmetic that guards [`crate::ClockSource::sample_to_ptp`] /
16///   [`crate::ClockSource::ptp_to_sample`] against overflow. Represented by
17///   [`ClockError::InvalidSampleRate`], [`ClockError::ConversionOverflow`], and
18///   the core-delegated [`ClockError::Core`].
19#[derive(Debug, thiserror::Error)]
20pub enum ClockError {
21    /// No clock source is available on this host (e.g. no PTP daemon running,
22    /// no PTP-capable NIC).
23    #[error("clock source unavailable: {0}")]
24    Unavailable(String),
25
26    /// The clock exists but has not yet synchronised (e.g. NTP still
27    /// converging, PTP master not yet locked).
28    #[error("clock not yet synchronised: {0}")]
29    NotSynced(String),
30
31    /// The provided sample rate is not a positive, supported value.
32    #[error("invalid sample rate: {0} Hz")]
33    InvalidSampleRate(u32),
34
35    /// A sample-index ↔ timestamp conversion would overflow `i64`.
36    ///
37    /// This indicates an anchor or sample index absurdly far from the clock's
38    /// reference point; callers should re-anchor closer to the working range.
39    #[error("timestamp conversion overflow")]
40    ConversionOverflow,
41
42    /// A validation failure delegated to the shared core types.
43    #[error(transparent)]
44    Core(#[from] AudioError),
45
46    /// A raw I/O error from the operating system (e.g. reading a PTP device).
47    #[error(transparent)]
48    Io(#[from] io::Error),
49}
50
51/// Convenience alias used throughout the crate and by consumers.
52pub type Result<T> = core::result::Result<T, ClockError>;
53
54#[cfg(test)]
55mod tests {
56    use std::io;
57
58    use audio_core_bsd::AudioError;
59
60    use super::{ClockError, Result};
61
62    #[test]
63    fn unavailable_display_carries_message() {
64        let msg = ClockError::Unavailable("no ptpd".into()).to_string();
65        assert_eq!(msg, "clock source unavailable: no ptpd");
66    }
67
68    #[test]
69    fn not_synced_display_carries_message() {
70        let msg = ClockError::NotSynced("ntp converging".into()).to_string();
71        assert_eq!(msg, "clock not yet synchronised: ntp converging");
72    }
73
74    #[test]
75    fn invalid_sample_rate_display_includes_value() {
76        let msg = ClockError::InvalidSampleRate(0).to_string();
77        assert_eq!(msg, "invalid sample rate: 0 Hz");
78    }
79
80    #[test]
81    fn conversion_overflow_display() {
82        assert_eq!(
83            ClockError::ConversionOverflow.to_string(),
84            "timestamp conversion overflow"
85        );
86    }
87
88    #[test]
89    fn core_variant_wraps_audio_error() {
90        let err = ClockError::from(AudioError::InvalidSampleRate(0));
91        assert_eq!(err.to_string(), "invalid sample rate: 0 Hz");
92    }
93
94    #[test]
95    fn core_from_audio_error_preserves_identity() {
96        let core = AudioError::InvalidChannelCount(0);
97        let wrapped: ClockError = core.clone().into();
98        assert!(matches!(wrapped, ClockError::Core(ref e) if *e == core));
99    }
100
101    #[test]
102    fn io_variant_wraps_std_io_error() {
103        let io_err = io::Error::new(io::ErrorKind::NotFound, "missing");
104        let err = ClockError::from(io_err);
105        assert!(err.to_string().contains("missing"));
106    }
107
108    #[test]
109    fn result_alias_ok_carries_value() {
110        let ok: Result<u32> = Ok(7);
111        assert_eq!(ok.map(|v| v + 1).unwrap(), 8);
112    }
113
114    #[test]
115    fn result_alias_err_carries_clock_error() {
116        let err: Result<u32> = Err(ClockError::Unavailable("x".into()));
117        assert!(err.is_err());
118    }
119}