Skip to main content

win_nightlight_watch/
error.rs

1use crate::MAX_TIMEOUT_MILLIS;
2use windows_result::WIN32_ERROR;
3use windows_sys::Win32::Foundation::ERROR_FILE_NOT_FOUND;
4use windows_sys::Win32::Foundation::ERROR_KEY_DELETED;
5use windows_sys::Win32::Foundation::ERROR_PATH_NOT_FOUND;
6
7/// Result of a registry watcher operation.
8pub type Result<T, E = Error> = std::result::Result<T, E>;
9
10/// Report a watcher failure and preserve its underlying error.
11#[derive(Debug, thiserror::Error)]
12#[error(transparent)]
13pub struct Error(#[from] Repr);
14
15impl Error {
16    /// Return the category of this failure.
17    pub fn kind(&self) -> ErrorKind {
18        match &self.0 {
19            Repr::Timeout => ErrorKind::InvalidInput,
20            Repr::Read { .. } => ErrorKind::Read,
21            Repr::WaitStatus(_) => ErrorKind::Windows,
22            Repr::Open { source }
23            | Repr::CreateEvent { source }
24            | Repr::ResetEvent { source }
25            | Repr::Register { source }
26            | Repr::Wait { source } => match WIN32_ERROR::from_error(source).map(|code| code.0) {
27                Some(ERROR_FILE_NOT_FOUND | ERROR_PATH_NOT_FOUND) => ErrorKind::NotFound,
28                Some(ERROR_KEY_DELETED) => ErrorKind::KeyDeleted,
29                _ => ErrorKind::Windows,
30            },
31        }
32    }
33}
34
35/// Classify a watcher failure.
36#[non_exhaustive]
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum ErrorKind {
39    /// The requested registry key does not exist.
40    NotFound,
41    /// Windows reports that the watched key was deleted.
42    KeyDeleted,
43    /// The timeout exceeds the finite range.
44    InvalidInput,
45    /// A Windows registry or event operation failed.
46    Windows,
47    /// Reading or decoding the snapshot failed; notification registration
48    /// remains active.
49    Read,
50}
51
52#[derive(Debug, thiserror::Error)]
53pub(crate) enum Repr {
54    #[error("timeout exceeds {MAX_TIMEOUT_MILLIS} milliseconds")]
55    Timeout,
56    #[error("opening the registry key for notifications")]
57    Open { source: windows_result::Error },
58    #[error("creating the notification event")]
59    CreateEvent { source: windows_result::Error },
60    #[error("resetting the notification event")]
61    ResetEvent { source: windows_result::Error },
62    #[error("reading the Night Light snapshot")]
63    Read { source: win_nightlight_lib::Error },
64    #[error("registering for registry changes")]
65    Register { source: windows_result::Error },
66    #[error("waiting for a registry notification")]
67    Wait { source: windows_result::Error },
68    #[error("unexpected notification wait result {0:#x}")]
69    WaitStatus(u32),
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75    use std::error::Error as _;
76    use test_case::test_case;
77
78    #[test]
79    fn error_is_send_and_sync() {
80        fn assert_send_sync<T: Send + Sync + 'static>() {}
81        assert_send_sync::<Error>();
82    }
83
84    #[test_case(2, ErrorKind::NotFound)]
85    #[test_case(3, ErrorKind::NotFound)]
86    #[test_case(1018, ErrorKind::KeyDeleted)]
87    #[test_case(5, ErrorKind::Windows)]
88    fn windows_errors_preserve_category_and_code(code: u32, expected: ErrorKind) {
89        let error = Error::from(Repr::Register {
90            source: WIN32_ERROR(code).into(),
91        });
92        assert_eq!(error.kind(), expected);
93        assert_eq!(error.to_string(), "registering for registry changes");
94        let source = error
95            .source()
96            .expect("Windows error is preserved")
97            .downcast_ref::<windows_result::Error>()
98            .expect("source is a Windows error");
99        assert_eq!(WIN32_ERROR::from_error(source), Some(WIN32_ERROR(code)));
100    }
101}