1#![macro_use]
2
3use libc::{c_char, c_int, c_void, free};
4use std::error::Error as StdError;
5use std::ffi::CStr;
6use std::{fmt, str};
7
8/// ALSA error
9///
10/// Most ALSA functions can return a negative error code.
11/// If so, then that error code is wrapped into this `Error` struct.
12/// An Error is also returned in case ALSA returns a string that
13/// cannot be translated into Rust's UTF-8 strings.
14#[derive(Clone, PartialEq, Copy)]
15pub struct Error(&'static str, i32);
16
17pub type Result<T> = ::std::result::Result<T, Error>;
18
19macro_rules! acheck {
20 ($f: ident ( $($x: expr),* ) ) => {{
21 let r = unsafe { alsa::$f( $($x),* ) };
22 if r < 0 { Err(Error::new(stringify!($f), -r as ::libc::c_int)) }
23 else { Ok(r) }
24 }}
25}
26
27pub fn from_const<'a>(func: &'static str, s: *const c_char) -> Result<&'a str> {
28 if s.is_null() {
29 return Err(invalid_str(func));
30 };
31 let cc: &CStr = unsafe { CStr::from_ptr(s) };
32 str::from_utf8(cc.to_bytes()).map_err(|_| invalid_str(func))
33}
34
35pub fn from_alloc(func: &'static str, s: *mut c_char) -> Result<String> {
36 if s.is_null() {
37 return Err(invalid_str(func));
38 };
39 let c: &CStr = unsafe { CStr::from_ptr(s) };
40 let ss: String = str&str::from_utf8(c.to_bytes())
41 .map_err(|_| {
42 unsafe {
43 free(s as *mut c_void);
44 }
45 invalid_str(func)
46 })?
47 .to_string();
48 unsafe {
49 free(s as *mut c_void);
50 }
51 Ok(ss)
52}
53
54pub fn from_code(func: &'static str, r: c_int) -> Result<c_int> {
55 if r < 0 {
56 Err(Error::new(func, res:r))
57 } else {
58 Ok(r)
59 }
60}
61
62impl Error {
63 pub fn new(func: &'static str, res: c_int) -> Error {
64 Self(func, res)
65 }
66
67 pub fn last(func: &'static str) -> Error {
68 Self(
69 func,
70 std::io::Error::last_os_error()
71 .raw_os_error()
72 .unwrap_or_default(),
73 )
74 }
75
76 pub fn unsupported(func: &'static str) -> Error {
77 Error(func, libc::ENOTSUP)
78 }
79
80 /// The function which failed.
81 pub fn func(&self) -> &'static str {
82 self.0
83 }
84
85 /// Underlying error
86 ///
87 /// Match this against the re-export of `nix::Error` in this crate, not against a specific version
88 /// of the nix crate. The nix crate version might be updated with minor updates of this library.
89 pub fn errno(&self) -> i32 {
90 self.1
91 }
92}
93
94pub fn invalid_str(func: &'static str) -> Error {
95 Error(func, libc::EILSEQ)
96}
97
98impl StdError for Error {
99 fn description(&self) -> &str {
100 "ALSA error"
101 }
102}
103
104impl fmt::Debug for Error {
105 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106 write!(f, "{}", self)
107 }
108}
109
110impl fmt::Display for Error {
111 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
112 write!(
113 f,
114 "ALSA function '{}' failed with error '{} ({})'",
115 self.0,
116 desc(self.1),
117 self.1,
118 )
119 }
120}
121
122/// See <https://github.com/nix-rust/nix/blob/197f55b3ccbce3273bf6ce119d1a8541b5df5d66/src/errno.rs#L198>
123///
124/// Note this doesn't include the total set of possible errno variants, but they
125/// can easily be added in the future for better error messages
126fn desc(errno: i32) -> &'static str {
127 match errno {
128 libc::EPERM => "Operation not permitted",
129 libc::ENOENT => "No such file or directory",
130 libc::ESRCH => "No such process",
131 libc::EINTR => "Interrupted system call",
132 libc::EIO => "I/O error",
133 libc::ENXIO => "No such device or address",
134 libc::E2BIG => "Argument list too long",
135 libc::ENOEXEC => "Exec format error",
136 libc::EBADF => "Bad file number",
137 libc::ECHILD => "No child processes",
138 libc::EAGAIN => "Try again",
139 libc::ENOMEM => "Out of memory",
140 libc::EACCES => "Permission denied",
141 libc::EFAULT => "Bad address",
142 libc::ENOTBLK => "Block device required",
143 libc::EBUSY => "Device or resource busy",
144 libc::EEXIST => "File exists",
145 libc::EXDEV => "Cross-device link",
146 libc::ENODEV => "No such device",
147 libc::ENOTDIR => "Not a directory",
148 libc::EISDIR => "Is a directory",
149 libc::EINVAL => "Invalid argument",
150 libc::ENFILE => "File table overflow",
151 libc::EMFILE => "Too many open files",
152 libc::ENOTTY => "Not a typewriter",
153 libc::ETXTBSY => "Text file busy",
154 libc::EFBIG => "File too large",
155 libc::ENOSPC => "No space left on device",
156 libc::ESPIPE => "Illegal seek",
157 libc::EROFS => "Read-only file system",
158 libc::EMLINK => "Too many links",
159 libc::EPIPE => "Broken pipe",
160 libc::EDOM => "Math argument out of domain of func",
161 libc::ERANGE => "Math result not representable",
162 libc::EDEADLK => "Resource deadlock would occur",
163 libc::ENAMETOOLONG => "File name too long",
164 libc::ENOLCK => "No record locks available",
165 libc::ENOSYS => "Function not implemented",
166 libc::ENOTEMPTY => "Directory not empty",
167 libc::ELOOP => "Too many symbolic links encountered",
168 libc::ENOMSG => "No message of desired type",
169 libc::EIDRM => "Identifier removed",
170 libc::EINPROGRESS => "Operation now in progress",
171 libc::EALREADY => "Operation already in progress",
172 libc::ENOTSOCK => "Socket operation on non-socket",
173 libc::EDESTADDRREQ => "Destination address required",
174 libc::EMSGSIZE => "Message too long",
175 libc::EPROTOTYPE => "Protocol wrong type for socket",
176 libc::ENOPROTOOPT => "Protocol not available",
177 libc::EPROTONOSUPPORT => "Protocol not supported",
178 libc::ESOCKTNOSUPPORT => "Socket type not supported",
179 libc::EPFNOSUPPORT => "Protocol family not supported",
180 libc::EAFNOSUPPORT => "Address family not supported by protocol",
181 libc::EADDRINUSE => "Address already in use",
182 libc::EADDRNOTAVAIL => "Cannot assign requested address",
183 libc::ENETDOWN => "Network is down",
184 libc::ENETUNREACH => "Network is unreachable",
185 libc::ENETRESET => "Network dropped connection because of reset",
186 libc::ECONNABORTED => "Software caused connection abort",
187 libc::ECONNRESET => "Connection reset by peer",
188 libc::ENOBUFS => "No buffer space available",
189 libc::EISCONN => "Transport endpoint is already connected",
190 libc::ENOTCONN => "Transport endpoint is not connected",
191 libc::ESHUTDOWN => "Cannot send after transport endpoint shutdown",
192 libc::ETOOMANYREFS => "Too many references: cannot splice",
193 libc::ETIMEDOUT => "Connection timed out",
194 libc::ECONNREFUSED => "Connection refused",
195 libc::EHOSTDOWN => "Host is down",
196 libc::EHOSTUNREACH => "No route to host",
197 libc::ENOTSUP => "Operation not supported",
198 _ => "Unknown errno",
199 }
200}
201
202impl From<Error> for fmt::Error {
203 fn from(_: Error) -> fmt::Error {
204 fmt::Error
205 }
206}
207
208#[test]
209fn broken_pcm_name() {
210 use std::ffi::CString;
211 let e: Error = crateOption::PCM::open(
212 &*CString::new("this_PCM_does_not_exist").unwrap(),
213 dir:crate::Direction::Playback,
214 nonblock:false,
215 )
216 .err()
217 .unwrap();
218 assert_eq!(e.func(), "snd_pcm_open");
219 assert_eq!(e.errno(), libc::ENOENT);
220}
221