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}