Repository navigation
feat(error): unified errors with precise kinds, messages, and no panics - #1167
Conversation
|
In this implementation I tried to address that use case by capturing If in addition to that we'd add I feel that with the |
|
I see, in that case I think we should make sure that host implementations don't forget to attach "context" when propogating error up, which I think we could do with some |
I think we have that, right? There are
Good idea, I'll look into it. |
|
I meant something like this: // I type this directly in github so treat this as more like pseudo-code
impl Error {
pub fn context(self, msg: &str) -> Self {
self.message = format!("{}\nCaused by: {}", msg, self);
self
}
}It is similar to |
|
I'm not sure the "Caused by:" format or similar is the right fit for cpal. As a general-purpose library, formatting error messages should be the downstream app's choice. If the concern is that backends might lose context when propagating errors, I think they already handle that today via |
Replace per-operation error types and the HostUnavailable struct with a unified Error/ErrorKind inspired by std::io::Error. Also replace poisoned-lock panics in play/pause/buffer_size with StreamInvalidated errors, or with graceful exits or silent skips where Result propagation is not possible
ab9c07e to
e9c9971
Compare
Well the function I proposed is just a helper for propogating errors so when we propogate errors we dont have to do: pub(crate) fn build_output_stream_raw_inner() {
// -- snip --
let max_frames_in_buffer = audio_client.GetBufferSize().map_err(|e| {
windows_err_to_cpal_err_message::<BuildStreamError>(
e,
"failed to obtain buffer size: ",
)
})?;
// We can shorten it into
let max_frames_in_buffer = audio_client.GetBufferSize().context("failed to obtain buffer size")?;
}Although to have the above syntax work we need to redefine the helper function to an extension trait pub trait ErrorExt{...}
impl<T, E: Display> ErrorExt for Result<T, E> {
fn context(self, msg: &str) -> Result<T, Error> {...}
}If a specific host want to process the inner error, they have option to format the string manually and call |
|
Ah, as convenience method. Yeah, we can do that. |
|
I'm going to merge this so it gets some exposure. Please open an issue for any further points. |
| pub(crate) trait ResultExt<T> { | ||
| /// Converts the error via [`Into<cpal::Error>`] and prepends `msg`, yielding | ||
| /// `"<msg>: <original error>"` as the message. | ||
| fn context(self, msg: &'static str) -> Result<T, Error>; |
There was a problem hiding this comment.
Is there a particular reason it need to be 'static?
There was a problem hiding this comment.
We didn't need more now but we can make it impl Display.
There was a problem hiding this comment.
I think we could remove the lifetime here and it would still compile. We don't need the static lifetime for the extension to work and it restrict the user to only string literals
| fn context(self, msg: &'static str) -> Result<T, Error>; | |
| fn context(self, msg: &str) -> Result<T, Error>; |
we can make it impl Display.
This could work too
There was a problem hiding this comment.
I'll change it to impl Display on master. Thanks for thinking along.
This is my last design goal for v0.18: a unified error type inspired by
std::io::Error.This new
cpal::ErrorreplacesHostUnavailable,DevicesError,SupportedStreamConfigsError, and all other per-operation error types in favor of a singleError/ErrorKindwith an optional descriptive message.While there, I took the opportunity to reduce the number of poisoned-lock panics, and making the timestamps on PipeWire infallible.