libpulse_binding/
error.rs

1// Copyright 2017 Lyndon Brown
2//
3// This file is part of the PulseAudio Rust language binding.
4//
5// Licensed under the MIT license or the Apache license (version 2.0), at your option. You may not
6// copy, modify, or distribute this file except in compliance with said license. You can find copies
7// of these licenses either in the LICENSE-MIT and LICENSE-APACHE files, or alternatively at
8// <http://opensource.org/licenses/MIT> and <http://www.apache.org/licenses/LICENSE-2.0>
9// respectively.
10//
11// Portions of documentation are copied from the LGPL 2.1+ licensed PulseAudio C headers on a
12// fair-use basis, as discussed in the overall project readme (available in the git repository).
13
14//! Error management.
15
16use std::ffi::CStr;
17use num_traits::FromPrimitive as FromPrimitiveTrait;
18use num_derive::{FromPrimitive, ToPrimitive};
19
20/// A wrapper around integer errors returned by PulseAudio. Can be converted to a [`Code`] variant
21/// for comparison purposes if desired.
22#[repr(transparent)]
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24pub struct PAErr(pub i32);
25
26/// These represent errors returned by many of the underlying PulseAudio C functions.
27#[repr(C)]
28#[derive(Debug, Copy, Clone, PartialEq, Eq)]
29#[derive(FromPrimitive, ToPrimitive)]
30#[allow(non_camel_case_types)]
31pub enum Code {
32    /* NOTE: This enum’s variants and variant values **must** remain identical to the `sys` crate
33       (C API) equivalent */
34    /// No error.
35    Ok = 0,
36    /// Access failure.
37    Access,
38    /// Unknown command.
39    Command,
40    /// Invalid argument.
41    Invalid,
42    /// Entity exists.
43    Exist,
44    /// No such entity.
45    NoEntity,
46    /// Connection refused.
47    ConnectionRefused,
48    /// Protocol error.
49    Protocol,
50    /// Timeout.
51    Timeout,
52    /// No authentication key.
53    AuthKey,
54    /// Internal.
55    Internal,
56    /// Connection terminated.
57    ConnectionTerminated,
58    /// Entity killed.
59    Killed,
60    /// Invalid server.
61    InvalidServer,
62    /// Module init failed.
63    ModInitFailed,
64    /// Bad state.
65    BadState,
66    /// No data.
67    NoData,
68    /// Incompatible protocol version.
69    Version,
70    /// Data too large.
71    TooLarge,
72    /// Operation not supported.
73    NotSupported,
74    /// The error code was unknown to the client.
75    Unknown,
76    /// Extension does not exist.
77    NoExtension,
78    /// Obsolete functionality.
79    Obsolete,
80    /// Missing implementation.
81    NotImplemented,
82    /// The caller forked without calling execve() and tried to reuse the context.
83    Forked,
84    /// An IO error happened.
85    IO,
86    /// Device or resource busy.
87    Busy,
88}
89
90/// Check is equal to `sys` equivalent
91#[test]
92fn code_compare_capi() {
93    assert_eq!(std::mem::size_of::<Code>(), std::mem::size_of::<capi::pa_error_code_t>());
94    assert_eq!(std::mem::align_of::<Code>(), std::mem::align_of::<capi::pa_error_code_t>());
95
96    // Check order and value of variants match
97    // No point checking conversions in both directions since both are a transmute
98    assert_eq!(Code::Ok,                   Code::from(capi::pa_error_code_t::Ok));
99    assert_eq!(Code::Access,               Code::from(capi::pa_error_code_t::Access));
100    assert_eq!(Code::Command,              Code::from(capi::pa_error_code_t::Command));
101    assert_eq!(Code::Invalid,              Code::from(capi::pa_error_code_t::Invalid));
102    assert_eq!(Code::Exist,                Code::from(capi::pa_error_code_t::Exist));
103    assert_eq!(Code::NoEntity,             Code::from(capi::pa_error_code_t::NoEntity));
104    assert_eq!(Code::ConnectionRefused,    Code::from(capi::pa_error_code_t::ConnectionRefused));
105    assert_eq!(Code::Protocol,             Code::from(capi::pa_error_code_t::Protocol));
106    assert_eq!(Code::Timeout,              Code::from(capi::pa_error_code_t::Timeout));
107    assert_eq!(Code::AuthKey,              Code::from(capi::pa_error_code_t::AuthKey));
108    assert_eq!(Code::Internal,             Code::from(capi::pa_error_code_t::Internal));
109    assert_eq!(Code::ConnectionTerminated, Code::from(capi::pa_error_code_t::ConnectionTerminated));
110    assert_eq!(Code::Killed,               Code::from(capi::pa_error_code_t::Killed));
111    assert_eq!(Code::InvalidServer,        Code::from(capi::pa_error_code_t::InvalidServer));
112    assert_eq!(Code::ModInitFailed,        Code::from(capi::pa_error_code_t::ModInitFailed));
113    assert_eq!(Code::BadState,             Code::from(capi::pa_error_code_t::BadState));
114    assert_eq!(Code::NoData,               Code::from(capi::pa_error_code_t::NoData));
115    assert_eq!(Code::Version,              Code::from(capi::pa_error_code_t::Version));
116    assert_eq!(Code::TooLarge,             Code::from(capi::pa_error_code_t::TooLarge));
117    assert_eq!(Code::NotSupported,         Code::from(capi::pa_error_code_t::NotSupported));
118    assert_eq!(Code::Unknown,              Code::from(capi::pa_error_code_t::Unknown));
119    assert_eq!(Code::NoExtension,          Code::from(capi::pa_error_code_t::NoExtension));
120    assert_eq!(Code::Obsolete,             Code::from(capi::pa_error_code_t::Obsolete));
121    assert_eq!(Code::NotImplemented,       Code::from(capi::pa_error_code_t::NotImplemented));
122    assert_eq!(Code::Forked,               Code::from(capi::pa_error_code_t::Forked));
123    assert_eq!(Code::IO,                   Code::from(capi::pa_error_code_t::IO));
124    assert_eq!(Code::Busy,                 Code::from(capi::pa_error_code_t::Busy));
125}
126
127impl From<Code> for capi::pa_error_code_t {
128    #[inline]
129    fn from(c: Code) -> Self {
130        unsafe { std::mem::transmute(c) }
131    }
132}
133impl From<capi::pa_error_code_t> for Code {
134    #[inline]
135    fn from(c: capi::pa_error_code_t) -> Self {
136        unsafe { std::mem::transmute(c) }
137    }
138}
139
140impl PAErr {
141    /// Converts an integer error value, as returned by many PA C API functions, to a human readable
142    /// string.
143    pub fn to_string(&self) -> Option<String> {
144        let ptr = unsafe { capi::pa_strerror(self.0) };
145        match ptr.is_null() {
146            false => Some(unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() }),
147            true => None,
148        }
149    }
150}
151
152impl std::error::Error for PAErr {}
153
154impl std::fmt::Display for PAErr {
155    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
156        match self.to_string() {
157            Some(s) => write!(f, "{}", s),
158            None => write!(f, ""),
159        }
160    }
161}
162
163impl Code {
164    /// Converts a `Code` to a human readable string.
165    #[inline]
166    pub fn to_string(self) -> Option<String> {
167        PAErr::from(self).to_string()
168    }
169}
170
171impl std::error::Error for Code {}
172
173impl std::fmt::Display for Code {
174    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
175        match (*self).to_string() {
176            Some(s) => write!(f, "{}", s),
177            None => write!(f, ""),
178        }
179    }
180}
181
182impl From<Code> for PAErr {
183    #[inline]
184    fn from(c: Code) -> Self {
185        // Error codes are negative, `Code` enum variants are positive
186        PAErr(-(c as i32))
187    }
188}
189
190impl TryFrom<PAErr> for Code {
191    type Error = ();
192
193    /// Attempts to convert the wrapped integer error value to a `Code` variant.
194    ///
195    /// Returns `Err(())` if the value cannot be mapped.
196    #[inline]
197    fn try_from(e: PAErr) -> Result<Self, Self::Error> {
198        // Error codes are negative, `Code` enum variants are positive
199        let abs = -(e.0);
200        Code::from_i32(abs).ok_or(())
201    }
202}
203
204/// Check `PAErr` <=> `Code` conversions
205#[test]
206fn check_code_paerr_conversions() {
207    assert_eq!(Ok(Code::Ok),                   Code::try_from(PAErr(0)));
208    assert_eq!(Ok(Code::Access),               Code::try_from(PAErr(-1)));
209    assert_eq!(Ok(Code::Command),              Code::try_from(PAErr(-2)));
210    assert_eq!(Ok(Code::Invalid),              Code::try_from(PAErr(-3)));
211    assert_eq!(Ok(Code::Exist),                Code::try_from(PAErr(-4)));
212    assert_eq!(Ok(Code::NoEntity),             Code::try_from(PAErr(-5)));
213    assert_eq!(Ok(Code::ConnectionRefused),    Code::try_from(PAErr(-6)));
214    assert_eq!(Ok(Code::Protocol),             Code::try_from(PAErr(-7)));
215    assert_eq!(Ok(Code::Timeout),              Code::try_from(PAErr(-8)));
216    assert_eq!(Ok(Code::AuthKey),              Code::try_from(PAErr(-9)));
217    assert_eq!(Ok(Code::Internal),             Code::try_from(PAErr(-10)));
218    assert_eq!(Ok(Code::ConnectionTerminated), Code::try_from(PAErr(-11)));
219    assert_eq!(Ok(Code::Killed),               Code::try_from(PAErr(-12)));
220    assert_eq!(Ok(Code::InvalidServer),        Code::try_from(PAErr(-13)));
221    assert_eq!(Ok(Code::ModInitFailed),        Code::try_from(PAErr(-14)));
222    assert_eq!(Ok(Code::BadState),             Code::try_from(PAErr(-15)));
223    assert_eq!(Ok(Code::NoData),               Code::try_from(PAErr(-16)));
224    assert_eq!(Ok(Code::Version),              Code::try_from(PAErr(-17)));
225    assert_eq!(Ok(Code::TooLarge),             Code::try_from(PAErr(-18)));
226    assert_eq!(Ok(Code::NotSupported),         Code::try_from(PAErr(-19)));
227    assert_eq!(Ok(Code::Unknown),              Code::try_from(PAErr(-20)));
228    assert_eq!(Ok(Code::NoExtension),          Code::try_from(PAErr(-21)));
229    assert_eq!(Ok(Code::Obsolete),             Code::try_from(PAErr(-22)));
230    assert_eq!(Ok(Code::NotImplemented),       Code::try_from(PAErr(-23)));
231    assert_eq!(Ok(Code::Forked),               Code::try_from(PAErr(-24)));
232    assert_eq!(Ok(Code::IO),                   Code::try_from(PAErr(-25)));
233    assert_eq!(Ok(Code::Busy),                 Code::try_from(PAErr(-26)));
234    assert_eq!(Err(()),                        Code::try_from(PAErr(-27)));
235    assert_eq!(Err(()),                        Code::try_from(PAErr(1)));
236
237    assert_eq!(PAErr::from(Code::Ok),                   PAErr(0));
238    assert_eq!(PAErr::from(Code::Access),               PAErr(-1));
239    assert_eq!(PAErr::from(Code::Command),              PAErr(-2));
240    assert_eq!(PAErr::from(Code::Invalid),              PAErr(-3));
241    assert_eq!(PAErr::from(Code::Exist),                PAErr(-4));
242    assert_eq!(PAErr::from(Code::NoEntity),             PAErr(-5));
243    assert_eq!(PAErr::from(Code::ConnectionRefused),    PAErr(-6));
244    assert_eq!(PAErr::from(Code::Protocol),             PAErr(-7));
245    assert_eq!(PAErr::from(Code::Timeout),              PAErr(-8));
246    assert_eq!(PAErr::from(Code::AuthKey),              PAErr(-9));
247    assert_eq!(PAErr::from(Code::Internal),             PAErr(-10));
248    assert_eq!(PAErr::from(Code::ConnectionTerminated), PAErr(-11));
249    assert_eq!(PAErr::from(Code::Killed),               PAErr(-12));
250    assert_eq!(PAErr::from(Code::InvalidServer),        PAErr(-13));
251    assert_eq!(PAErr::from(Code::ModInitFailed),        PAErr(-14));
252    assert_eq!(PAErr::from(Code::BadState),             PAErr(-15));
253    assert_eq!(PAErr::from(Code::NoData),               PAErr(-16));
254    assert_eq!(PAErr::from(Code::Version),              PAErr(-17));
255    assert_eq!(PAErr::from(Code::TooLarge),             PAErr(-18));
256    assert_eq!(PAErr::from(Code::NotSupported),         PAErr(-19));
257    assert_eq!(PAErr::from(Code::Unknown),              PAErr(-20));
258    assert_eq!(PAErr::from(Code::NoExtension),          PAErr(-21));
259    assert_eq!(PAErr::from(Code::Obsolete),             PAErr(-22));
260    assert_eq!(PAErr::from(Code::NotImplemented),       PAErr(-23));
261    assert_eq!(PAErr::from(Code::Forked),               PAErr(-24));
262    assert_eq!(PAErr::from(Code::IO),                   PAErr(-25));
263    assert_eq!(PAErr::from(Code::Busy),                 PAErr(-26));
264}