Skip to main content

token_privilege/
error.rs

1/// Errors that can occur when querying token privileges.
2///
3/// This enum is `#[non_exhaustive]` to allow adding new error variants in
4/// future minor releases without breaking downstream code. Always include
5/// a wildcard arm when matching.
6///
7/// # Examples
8///
9/// ```rust
10/// use token_privilege::TokenPrivilegeError;
11///
12/// fn handle_error(err: TokenPrivilegeError) {
13///     match err {
14///         TokenPrivilegeError::UnsupportedPlatform => {
15///             eprintln!("This feature requires Windows");
16///         }
17///         other => eprintln!("Token error: {other}"),
18///     }
19/// }
20/// ```
21#[derive(Debug, thiserror::Error)]
22#[non_exhaustive]
23pub enum TokenPrivilegeError {
24    /// Failed to open the process token.
25    ///
26    /// This typically occurs if the process lacks sufficient permissions
27    /// to query its own token (very rare in practice).
28    #[error("failed to open process token: {0}")]
29    OpenTokenFailed(std::io::Error),
30
31    /// Failed to query token information.
32    ///
33    /// Returned when `GetTokenInformation` fails after the token
34    /// has been successfully opened.
35    #[error("failed to query token information: {0}")]
36    QueryFailed(std::io::Error),
37
38    /// The specified privilege name is invalid or not recognized.
39    ///
40    /// The Windows privilege name (e.g., `"SeDebugPrivilege"`) was not
41    /// found in the system's privilege database.
42    #[error("invalid privilege name: {name}")]
43    InvalidPrivilegeName {
44        /// The privilege name that was not recognized.
45        name: String,
46    },
47
48    /// Failed to look up the privilege value for the given name.
49    ///
50    /// Unlike [`InvalidPrivilegeName`](Self::InvalidPrivilegeName), this
51    /// indicates an OS-level failure during the lookup rather than an
52    /// unknown privilege name.
53    #[error("privilege lookup failed for '{name}'")]
54    LookupFailed {
55        /// The privilege name that was being looked up.
56        name: String,
57        /// The underlying OS error.
58        source: std::io::Error,
59    },
60
61    /// Failed to check privilege status.
62    ///
63    /// Returned when `PrivilegeCheck` fails after the privilege LUID
64    /// has been successfully resolved.
65    #[error("privilege check failed: {0}")]
66    CheckFailed(std::io::Error),
67
68    /// This functionality is only available on Windows.
69    ///
70    /// All public functions in this crate return this error on non-Windows
71    /// platforms. This allows downstream crates to depend on `token-privilege`
72    /// unconditionally and handle the non-Windows case gracefully.
73    #[error("token privilege operations are only supported on Windows")]
74    UnsupportedPlatform,
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn unsupported_platform_display() {
83        let err = TokenPrivilegeError::UnsupportedPlatform;
84        let msg = err.to_string();
85        assert!(
86            msg.contains("only supported on Windows"),
87            "unexpected message: {msg}"
88        );
89    }
90
91    #[test]
92    fn open_token_failed_display() {
93        let io_err = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "access denied");
94        let err = TokenPrivilegeError::OpenTokenFailed(io_err);
95        let msg = err.to_string();
96        assert!(
97            msg.contains("open process token"),
98            "unexpected message: {msg}"
99        );
100    }
101
102    #[test]
103    fn invalid_privilege_name_display() {
104        let err = TokenPrivilegeError::InvalidPrivilegeName {
105            name: "FakePrivilege".to_owned(),
106        };
107        let msg = err.to_string();
108        assert!(msg.contains("FakePrivilege"), "unexpected message: {msg}");
109    }
110
111    #[test]
112    fn query_failed_display() {
113        let io_err = std::io::Error::other("query error");
114        let err = TokenPrivilegeError::QueryFailed(io_err);
115        let msg = err.to_string();
116        assert!(
117            msg.contains("query token information"),
118            "unexpected message: {msg}"
119        );
120    }
121
122    #[test]
123    fn lookup_failed_display() {
124        let io_err = std::io::Error::other("not found");
125        let err = TokenPrivilegeError::LookupFailed {
126            name: "SeDebugPrivilege".to_owned(),
127            source: io_err,
128        };
129        let msg = err.to_string();
130        assert!(
131            msg.contains("SeDebugPrivilege"),
132            "unexpected message: {msg}"
133        );
134        // Verify the source error is accessible via Error::source(), not duplicated in display
135        assert!(
136            !msg.contains("not found"),
137            "display should not contain source text to avoid duplication: {msg}"
138        );
139    }
140
141    #[test]
142    fn check_failed_display() {
143        let io_err = std::io::Error::other("check error");
144        let err = TokenPrivilegeError::CheckFailed(io_err);
145        let msg = err.to_string();
146        assert!(
147            msg.contains("privilege check failed"),
148            "unexpected message: {msg}"
149        );
150    }
151
152    #[test]
153    fn error_is_send_and_sync() {
154        fn assert_send_sync<T: Send + Sync>() {}
155        assert_send_sync::<TokenPrivilegeError>();
156    }
157}