1use std::fmt;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum MlockError {
10 PermissionDenied,
20
21 ResourceLimit,
34
35 InvalidArgument,
40
41 WouldBlock,
45
46 Unknown(i32),
50}
51
52impl MlockError {
53 #[must_use]
55 pub const fn from_errno(errno: i32) -> Self {
56 match errno {
57 libc::EPERM => Self::PermissionDenied,
58 libc::ENOMEM => Self::ResourceLimit,
59 libc::EINVAL => Self::InvalidArgument,
60 libc::EAGAIN => Self::WouldBlock,
61 _ => Self::Unknown(errno),
62 }
63 }
64
65 #[must_use]
67 pub const fn errno(&self) -> Option<i32> {
68 match self {
69 Self::PermissionDenied => Some(libc::EPERM),
70 Self::ResourceLimit => Some(libc::ENOMEM),
71 Self::InvalidArgument => Some(libc::EINVAL),
72 Self::WouldBlock => Some(libc::EAGAIN),
73 Self::Unknown(e) => Some(*e),
74 }
75 }
76
77 #[must_use]
79 pub const fn is_permission_error(&self) -> bool {
80 matches!(self, Self::PermissionDenied)
81 }
82
83 #[must_use]
85 pub const fn is_resource_limit(&self) -> bool {
86 matches!(self, Self::ResourceLimit)
87 }
88}
89
90impl fmt::Display for MlockError {
91 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92 match self {
93 Self::PermissionDenied => write!(
94 f,
95 "permission denied: need CAP_IPC_LOCK capability or root \
96 (docker: --cap-add=IPC_LOCK)"
97 ),
98 Self::ResourceLimit => write!(
99 f,
100 "resource limit: RLIMIT_MEMLOCK too low or insufficient memory \
101 (docker: --ulimit memlock=-1:-1)"
102 ),
103 Self::InvalidArgument => write!(f, "invalid argument: unsupported flags for this kernel"),
104 Self::WouldBlock => write!(f, "would block: some pages could not be locked"),
105 Self::Unknown(errno) => write!(f, "mlock failed with errno={errno}"),
106 }
107 }
108}
109
110impl std::error::Error for MlockError {}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115
116 #[test]
117 fn test_from_errno_eperm() {
118 let err = MlockError::from_errno(libc::EPERM);
119 assert_eq!(err, MlockError::PermissionDenied);
120 assert!(err.is_permission_error());
121 assert!(!err.is_resource_limit());
122 }
123
124 #[test]
125 fn test_from_errno_enomem() {
126 let err = MlockError::from_errno(libc::ENOMEM);
127 assert_eq!(err, MlockError::ResourceLimit);
128 assert!(!err.is_permission_error());
129 assert!(err.is_resource_limit());
130 }
131
132 #[test]
133 fn test_from_errno_einval() {
134 let err = MlockError::from_errno(libc::EINVAL);
135 assert_eq!(err, MlockError::InvalidArgument);
136 assert!(!err.is_permission_error());
137 assert!(!err.is_resource_limit());
138 }
139
140 #[test]
141 fn test_from_errno_eagain() {
142 let err = MlockError::from_errno(libc::EAGAIN);
143 assert_eq!(err, MlockError::WouldBlock);
144 assert!(!err.is_permission_error());
145 assert!(!err.is_resource_limit());
146 }
147
148 #[test]
149 fn test_from_errno_unknown() {
150 let err = MlockError::from_errno(999);
151 assert_eq!(err, MlockError::Unknown(999));
152 assert_eq!(err.errno(), Some(999));
153 }
154
155 #[test]
156 fn test_errno_all_variants() {
157 assert_eq!(MlockError::PermissionDenied.errno(), Some(libc::EPERM));
158 assert_eq!(MlockError::ResourceLimit.errno(), Some(libc::ENOMEM));
159 assert_eq!(MlockError::InvalidArgument.errno(), Some(libc::EINVAL));
160 assert_eq!(MlockError::WouldBlock.errno(), Some(libc::EAGAIN));
161 assert_eq!(MlockError::Unknown(42).errno(), Some(42));
162 }
163
164 #[test]
165 fn test_display_permission_denied() {
166 let err = MlockError::PermissionDenied;
167 let msg = format!("{err}");
168 assert!(msg.contains("CAP_IPC_LOCK"));
169 assert!(msg.contains("--cap-add=IPC_LOCK"));
170 }
171
172 #[test]
173 fn test_display_resource_limit() {
174 let err = MlockError::ResourceLimit;
175 let msg = format!("{err}");
176 assert!(msg.contains("RLIMIT_MEMLOCK"));
177 assert!(msg.contains("--ulimit memlock=-1:-1"));
178 }
179
180 #[test]
181 fn test_display_invalid_argument() {
182 let err = MlockError::InvalidArgument;
183 let msg = format!("{err}");
184 assert!(msg.contains("invalid argument"));
185 }
186
187 #[test]
188 fn test_display_would_block() {
189 let err = MlockError::WouldBlock;
190 let msg = format!("{err}");
191 assert!(msg.contains("would block"));
192 }
193
194 #[test]
195 fn test_display_unknown() {
196 let err = MlockError::Unknown(42);
197 let msg = format!("{err}");
198 assert!(msg.contains("errno=42"));
199 }
200
201 #[test]
202 fn test_error_trait() {
203 let err: &dyn std::error::Error = &MlockError::PermissionDenied;
204 assert!(!err.to_string().is_empty());
206 }
207}