win_nightlight_watch/
error.rs1use 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
7pub type Result<T, E = Error> = std::result::Result<T, E>;
9
10#[derive(Debug, thiserror::Error)]
12#[error(transparent)]
13pub struct Error(#[from] Repr);
14
15impl Error {
16 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#[non_exhaustive]
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
38pub enum ErrorKind {
39 NotFound,
41 KeyDeleted,
43 InvalidInput,
45 Windows,
47 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}