Skip to content

Commit 0792dba

Browse files
authored
audio: rename AudioErrorResult to AudioResult and make non_exhaustive (#441)
Using the new `num_enum` `catch_all` we can now automatically implement the `match` statement and get rid of one superfluous `AudioError` variant.
1 parent a7ce644 commit 0792dba

File tree

3 files changed

+75
-56
lines changed

3 files changed

+75
-56
lines changed

ndk/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
- Add `DataSpace` type and relevant functions on `Bitmap` and `NativeWindow`. (#438)
3737
- bitmap: Add `Bitmap::compress()` and `Bitmap::compress_raw()` functions. (#440)
3838
- **Breaking:** Turn `BitmapError` into a `non_exhaustive` `enum`. (#440)
39+
- **Breaking:** audio: Rename `AudioErrorResult` to `AudioResult` and turn into a `non_exhaustive` `enum`. (#441)
3940

4041
# 0.7.0 (2022-07-24)
4142

ndk/src/audio.rs

Lines changed: 41 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::{
1717
ptr::NonNull,
1818
};
1919

20-
use num_enum::{IntoPrimitive, TryFromPrimitive};
20+
use num_enum::{FromPrimitive, IntoPrimitive, TryFromPrimitive};
2121
use thiserror::Error;
2222

2323
use crate::utils::abort_on_panic;
@@ -353,9 +353,10 @@ pub enum AudioCallbackResult {
353353
}
354354

355355
#[repr(i32)]
356-
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
356+
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
357+
#[non_exhaustive]
357358
#[doc(alias = "aaudio_result_t")]
358-
pub enum AudioErrorResult {
359+
pub enum AudioResult {
359360
#[doc(alias = "AAUDIO_ERROR_BASE")]
360361
Base = ffi::AAUDIO_ERROR_BASE,
361362
/// The audio device was disconnected. This could occur, for example, when headphones
@@ -409,58 +410,44 @@ pub enum AudioErrorResult {
409410
/// The requested sample rate was not supported.
410411
#[doc(alias = "AAUDIO_ERROR_INVALID_RATE")]
411412
InvalidRate = ffi::AAUDIO_ERROR_INVALID_RATE,
413+
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
414+
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
415+
#[num_enum(catch_all)]
416+
Unknown(i32) = ffi::AAUDIO_OK,
412417
}
413418

414-
impl AudioErrorResult {
419+
impl AudioResult {
415420
#[doc(alias = "AAudio_convertStreamStateToText")]
416421
pub fn to_text(self) -> Cow<'static, str> {
417-
let ptr = unsafe {
418-
CStr::from_ptr(ffi::AAudio_convertStreamStateToText(
419-
self as ffi::aaudio_result_t,
420-
))
421-
};
422+
let ptr = unsafe { CStr::from_ptr(ffi::AAudio_convertStreamStateToText(self.into())) };
422423
ptr.to_string_lossy()
423424
}
425+
426+
/// Returns [`Ok`] on [`ffi::AAUDIO_OK`], [`Err`] otherwise (including positive values).
427+
///
428+
/// Note that some known error codes (currently only for `AMediaCodec`) are positive.
429+
pub(crate) fn from_result(status: ffi::aaudio_result_t) -> Result<(), Self> {
430+
match status {
431+
ffi::AAUDIO_OK => Ok(()),
432+
x => Err(Self::from(x)),
433+
}
434+
}
424435
}
425436

426437
#[derive(Debug, Error)]
427438
pub enum AudioError {
428439
#[error("error Audio result ({0:?})")]
429-
ErrorResult(AudioErrorResult),
430-
#[error("unknown AAudio error result ({0})")]
431-
UnknownResult(i32),
440+
ErrorResult(AudioResult),
432441
#[error("unsupported AAudio result value received ({0})")]
433442
UnsupportedValue(i32),
434443
}
435444

436445
impl AudioError {
437-
pub(crate) fn from_result<T>(
438-
result: ffi::aaudio_result_t,
439-
on_success: impl FnOnce() -> T,
440-
) -> Result<T> {
441-
use AudioErrorResult::*;
442-
let result = match result {
443-
value if value >= 0 => return Ok(on_success()),
444-
ffi::AAUDIO_ERROR_BASE => Base,
445-
ffi::AAUDIO_ERROR_DISCONNECTED => Disconnected,
446-
ffi::AAUDIO_ERROR_ILLEGAL_ARGUMENT => IllegalArgument,
447-
ffi::AAUDIO_ERROR_INTERNAL => Internal,
448-
ffi::AAUDIO_ERROR_INVALID_STATE => InvalidState,
449-
ffi::AAUDIO_ERROR_INVALID_HANDLE => InvalidHandle,
450-
ffi::AAUDIO_ERROR_UNIMPLEMENTED => Unimplemented,
451-
ffi::AAUDIO_ERROR_UNAVAILABLE => Unavailable,
452-
ffi::AAUDIO_ERROR_NO_FREE_HANDLES => NoFreeHandles,
453-
ffi::AAUDIO_ERROR_NO_MEMORY => NoMemory,
454-
ffi::AAUDIO_ERROR_NULL => Null,
455-
ffi::AAUDIO_ERROR_TIMEOUT => Timeout,
456-
ffi::AAUDIO_ERROR_WOULD_BLOCK => WouldBlock,
457-
ffi::AAUDIO_ERROR_INVALID_FORMAT => InvalidFormat,
458-
ffi::AAUDIO_ERROR_OUT_OF_RANGE => OutOfRange,
459-
ffi::AAUDIO_ERROR_NO_SERVICE => NoService,
460-
ffi::AAUDIO_ERROR_INVALID_RATE => InvalidRate,
461-
_ => return Err(AudioError::UnknownResult(result)),
462-
};
463-
Err(AudioError::ErrorResult(result))
446+
/// Returns [`Ok`] on [`ffi::AAUDIO_OK`], [`Err`] otherwise (including positive values).
447+
///
448+
/// Note that some known error codes (currently only for `AMediaCodec`) are positive.
449+
pub(crate) fn from_result(status: ffi::aaudio_result_t) -> Result<()> {
450+
AudioResult::from_result(status).map_err(Self::ErrorResult)
464451
}
465452
}
466453

@@ -469,7 +456,7 @@ pub type Result<T, E = AudioError> = std::result::Result<T, E>;
469456
fn construct<T>(with_ptr: impl FnOnce(*mut T) -> ffi::aaudio_result_t) -> Result<T> {
470457
let mut result = MaybeUninit::uninit();
471458
let status = with_ptr(result.as_mut_ptr());
472-
AudioError::from_result(status, || unsafe { result.assume_init() })
459+
AudioError::from_result(status).map(|()| unsafe { result.assume_init() })
473460
}
474461

475462
fn enum_return_value<T: TryFrom<u32>>(return_value: i32) -> Result<T> {
@@ -754,7 +741,7 @@ impl AudioStreamBuilder {
754741
data_callback: None,
755742
error_callback: None,
756743
};
757-
let err = AudioError::from_result(error, || ()).unwrap_err();
744+
let err = AudioError::from_result(error).unwrap_err();
758745
(*callback)(&stream, err);
759746
std::mem::forget(stream);
760747
})
@@ -984,7 +971,7 @@ impl Drop for AudioStreamBuilder {
984971
#[doc(alias = "AAudioStreamBuilder_delete")]
985972
fn drop(&mut self) {
986973
let status = unsafe { ffi::AAudioStreamBuilder_delete(self.as_ptr()) };
987-
AudioError::from_result(status, || ()).unwrap();
974+
AudioError::from_result(status).unwrap();
988975
}
989976
}
990977

@@ -1209,12 +1196,12 @@ impl AudioStream {
12091196
/// It can also be used to align a recorded stream with a playback stream.
12101197
///
12111198
/// Timestamps are only valid when the stream is in `Started` state.
1212-
/// [`InvalidState`][AudioErrorResult::InvalidState] will be returned
1199+
/// [`InvalidState`][AudioResult::InvalidState] will be returned
12131200
/// if the stream is not started.
12141201
/// Note that because [`AudioStream::request_start()`] is asynchronous,
12151202
/// timestamps will not be valid until a short time after calling
12161203
/// [`AudioStream::request_start()`].
1217-
/// So [`InvalidState`][AudioErrorResult::InvalidState] should not be
1204+
/// So [`InvalidState`][AudioResult::InvalidState] should not be
12181205
/// considered a fatal error.
12191206
/// Just try calling again later.
12201207
///
@@ -1296,7 +1283,7 @@ impl AudioStream {
12961283
) -> Result<u32> {
12971284
let result = ffi::AAudioStream_read(self.as_ptr(), buffer, num_frames, timeout_nanoseconds);
12981285

1299-
AudioError::from_result(result, || result as u32)
1286+
AudioError::from_result(result).map(|()| result as u32)
13001287
}
13011288

13021289
/// Asynchronous request for the stream to flush.
@@ -1306,11 +1293,11 @@ impl AudioStream {
13061293
/// After this call the state will be in [`Flushing`][AudioStreamState::Flushing] or
13071294
/// [`Flushed`][AudioStreamState::Flushed].
13081295
///
1309-
/// This will return [`Unimplemented`][AudioErrorResult::Unimplemented] for input streams.
1296+
/// This will return [`Unimplemented`][AudioResult::Unimplemented] for input streams.
13101297
#[doc(alias = "AAudioStream_requestFlush")]
13111298
pub fn request_flush(&self) -> Result<()> {
13121299
let result = unsafe { ffi::AAudioStream_requestFlush(self.as_ptr()) };
1313-
AudioError::from_result(result, || ())
1300+
AudioError::from_result(result)
13141301
}
13151302

13161303
/// Asynchronous request for the stream to pause.
@@ -1319,12 +1306,12 @@ impl AudioStream {
13191306
/// After this call the state will be in [`Pausing`][AudioStreamState::Pausing] or
13201307
/// [`Paused`][AudioStreamState::Paused].
13211308
///
1322-
/// This will return [`Unimplemented`][AudioErrorResult::Unimplemented] for input streams.
1309+
/// This will return [`Unimplemented`][AudioResult::Unimplemented] for input streams.
13231310
/// For input streams use [`AudioStream::request_stop()`].
13241311
#[doc(alias = "AAudioStream_requestPause")]
13251312
pub fn request_pause(&self) -> Result<()> {
13261313
let result = unsafe { ffi::AAudioStream_requestPause(self.as_ptr()) };
1327-
AudioError::from_result(result, || ())
1314+
AudioError::from_result(result)
13281315
}
13291316

13301317
/// Asynchronously request to start playing the stream. For output streams, one should
@@ -1335,7 +1322,7 @@ impl AudioStream {
13351322
#[doc(alias = "AAudioStream_requestStart")]
13361323
pub fn request_start(&self) -> Result<()> {
13371324
let result = unsafe { ffi::AAudioStream_requestStart(self.as_ptr()) };
1338-
AudioError::from_result(result, || ())
1325+
AudioError::from_result(result)
13391326
}
13401327

13411328
/// Asynchronous request for the stream to stop.
@@ -1345,7 +1332,7 @@ impl AudioStream {
13451332
#[doc(alias = "AAudioStream_requestStop")]
13461333
pub fn request_stop(&self) -> Result<()> {
13471334
let result = unsafe { ffi::AAudioStream_requestStop(self.as_ptr()) };
1348-
AudioError::from_result(result, || ())
1335+
AudioError::from_result(result)
13491336
}
13501337

13511338
/// This can be used to adjust the latency of the buffer by changing
@@ -1366,7 +1353,7 @@ impl AudioStream {
13661353
#[doc(alias = "AAudioStream_setBufferSizeInFrames")]
13671354
pub fn set_buffer_size_in_frames(&self, num_frames: i32) -> Result<i32> {
13681355
let result = unsafe { ffi::AAudioStream_setBufferSizeInFrames(self.as_ptr(), num_frames) };
1369-
AudioError::from_result(result, || result)
1356+
AudioError::from_result(result).map(|()| result)
13701357
}
13711358

13721359
/// Wait until the current state no longer matches the input state.
@@ -1423,14 +1410,14 @@ impl AudioStream {
14231410
let result =
14241411
ffi::AAudioStream_write(self.as_ptr(), buffer, num_frames, timeout_nanoseconds);
14251412

1426-
AudioError::from_result(result, || result as u32)
1413+
AudioError::from_result(result).map(|()| result as u32)
14271414
}
14281415
}
14291416

14301417
impl Drop for AudioStream {
14311418
#[doc(alias = "AAudioStream_close")]
14321419
fn drop(&mut self) {
14331420
let status = unsafe { ffi::AAudioStream_close(self.as_ptr()) };
1434-
AudioError::from_result(status, || ()).unwrap();
1421+
AudioError::from_result(status).unwrap();
14351422
}
14361423
}

ndk/src/media_error.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,38 +15,69 @@ pub type Result<T, E = MediaError> = std::result::Result<T, E>;
1515
/// Media Status codes for [`media_status_t`](https://developer.android.com/ndk/reference/group/media#group___media_1ga009a49041fe39f7bdc6d8b5cddbe760c)
1616
#[repr(i32)]
1717
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive, IntoPrimitive)]
18+
#[doc(alias = "media_status_t")]
1819
#[non_exhaustive]
1920
pub enum MediaError {
21+
#[doc(alias = "AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE")]
2022
CodecErrorInsufficientResource = ffi::media_status_t::AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE.0,
23+
#[doc(alias = "AMEDIACODEC_ERROR_RECLAIMED")]
2124
CodecErrorReclaimed = ffi::media_status_t::AMEDIACODEC_ERROR_RECLAIMED.0,
25+
#[doc(alias = "AMEDIA_ERROR_UNKNOWN")]
2226
ErrorUnknown = ffi::media_status_t::AMEDIA_ERROR_UNKNOWN.0,
27+
#[doc(alias = "AMEDIA_ERROR_MALFORMED")]
2328
ErrorMalformed = ffi::media_status_t::AMEDIA_ERROR_MALFORMED.0,
29+
#[doc(alias = "AMEDIA_ERROR_UNSUPPORTED")]
2430
ErrorUnsupported = ffi::media_status_t::AMEDIA_ERROR_UNSUPPORTED.0,
31+
#[doc(alias = "AMEDIA_ERROR_INVALID_OBJECT")]
2532
ErrorInvalidObject = ffi::media_status_t::AMEDIA_ERROR_INVALID_OBJECT.0,
33+
#[doc(alias = "AMEDIA_ERROR_INVALID_PARAMETER")]
2634
ErrorInvalidParameter = ffi::media_status_t::AMEDIA_ERROR_INVALID_PARAMETER.0,
35+
#[doc(alias = "AMEDIA_ERROR_INVALID_OPERATION")]
2736
ErrorInvalidOperation = ffi::media_status_t::AMEDIA_ERROR_INVALID_OPERATION.0,
37+
#[doc(alias = "AMEDIA_ERROR_END_OF_STREAM")]
2838
ErrorEndOfStream = ffi::media_status_t::AMEDIA_ERROR_END_OF_STREAM.0,
39+
#[doc(alias = "AMEDIA_ERROR_IO")]
2940
ErrorIo = ffi::media_status_t::AMEDIA_ERROR_IO.0,
41+
#[doc(alias = "AMEDIA_ERROR_WOULD_BLOCK")]
3042
ErrorWouldBlock = ffi::media_status_t::AMEDIA_ERROR_WOULD_BLOCK.0,
43+
#[doc(alias = "AMEDIA_DRM_ERROR_BASE")]
3144
DrmErrorBase = ffi::media_status_t::AMEDIA_DRM_ERROR_BASE.0,
45+
#[doc(alias = "AMEDIA_DRM_NOT_PROVISIONED")]
3246
DrmNotProvisioned = ffi::media_status_t::AMEDIA_DRM_NOT_PROVISIONED.0,
47+
#[doc(alias = "AMEDIA_DRM_RESOURCE_BUSY")]
3348
DrmResourceBusy = ffi::media_status_t::AMEDIA_DRM_RESOURCE_BUSY.0,
49+
#[doc(alias = "AMEDIA_DRM_DEVICE_REVOKED")]
3450
DrmDeviceRevoked = ffi::media_status_t::AMEDIA_DRM_DEVICE_REVOKED.0,
51+
#[doc(alias = "AMEDIA_DRM_SHORT_BUFFER")]
3552
DrmShortBuffer = ffi::media_status_t::AMEDIA_DRM_SHORT_BUFFER.0,
53+
#[doc(alias = "AMEDIA_DRM_SESSION_NOT_OPENED")]
3654
DrmSessionNotOpened = ffi::media_status_t::AMEDIA_DRM_SESSION_NOT_OPENED.0,
55+
#[doc(alias = "AMEDIA_DRM_TAMPER_DETECTED")]
3756
DrmTamperDetected = ffi::media_status_t::AMEDIA_DRM_TAMPER_DETECTED.0,
57+
#[doc(alias = "AMEDIA_DRM_VERIFY_FAILED")]
3858
DrmVerifyFailed = ffi::media_status_t::AMEDIA_DRM_VERIFY_FAILED.0,
59+
#[doc(alias = "AMEDIA_DRM_NEED_KEY")]
3960
DrmNeedKey = ffi::media_status_t::AMEDIA_DRM_NEED_KEY.0,
61+
#[doc(alias = "AMEDIA_DRM_LICENSE_EXPIRED")]
4062
DrmLicenseExpired = ffi::media_status_t::AMEDIA_DRM_LICENSE_EXPIRED.0,
63+
#[doc(alias = "AMEDIA_IMGREADER_ERROR_BASE")]
4164
ImgreaderErrorBase = ffi::media_status_t::AMEDIA_IMGREADER_ERROR_BASE.0,
65+
#[doc(alias = "AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE")]
4266
ImgreaderNoBufferAvailable = ffi::media_status_t::AMEDIA_IMGREADER_NO_BUFFER_AVAILABLE.0,
67+
#[doc(alias = "AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED")]
4368
ImgreaderMaxImagesAcquired = ffi::media_status_t::AMEDIA_IMGREADER_MAX_IMAGES_ACQUIRED.0,
69+
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE")]
4470
ImgreaderCannotLockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_LOCK_IMAGE.0,
71+
#[doc(alias = "AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE")]
4572
ImgreaderCannotUnlockImage = ffi::media_status_t::AMEDIA_IMGREADER_CANNOT_UNLOCK_IMAGE.0,
73+
#[doc(alias = "AMEDIA_IMGREADER_IMAGE_NOT_LOCKED")]
4674
ImgreaderImageNotLocked = ffi::media_status_t::AMEDIA_IMGREADER_IMAGE_NOT_LOCKED.0,
47-
// Use the OK discriminant, assuming no-one calls `as i32` and only uses the generated `From` implementation via `IntoPrimitive`
75+
/// This error code is unknown to the [`ndk`][crate] crate. Please report an issue if you
76+
/// believe this code needs to be added to our mapping.
77+
// Use the OK discriminant, as no-one will be able to call `as i32` and only has access to the
78+
// constants via `From` provided by `IntoPrimitive` which reads the contained value.
4879
#[num_enum(catch_all)]
49-
Unknown(i32) = 0,
80+
Unknown(i32) = ffi::media_status_t::AMEDIA_OK.0,
5081
}
5182

5283
impl fmt::Display for MediaError {

0 commit comments

Comments
 (0)