Skip to content

Improve error handling in engine_api client using thiserror. Fixes #326 #338

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

zedddie16
Copy link

Improve engine_api errors

Improve errors in engine_api #326

(this is my second time trying to contribute, feedback is welcome)

  • run cargo fmt;
  • run cargo clippy;
  • run cargo testand all tests pass;
  • linked to the originating issue (if applicable).

@krsh krsh requested review from vadorovsky and banditopazzo April 14, 2025 11:57
@krsh krsh added the enhancement New feature or request label Apr 14, 2025
Copy link
Member

@banditopazzo banditopazzo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, thank you for your PR! 👋

In general, it's fine. I pointed out some small things that would be great if you could address before merging. Let me know if anything is unclear or if you need a hand with it—happy to help!

Thanks again for your contribution! 😊

@@ -1,6 +1,6 @@
use std::{ffi::CString, os::unix::prelude::FileTypeExt};

use anyhow::{Context, Result, anyhow, bail, ensure};
use anyhow::Result;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe this can be safely removed

}
std::io::ErrorKind::PermissionDenied => {
bail!("No write permission on '{socket}'")
return Err(EngineClientError::NoWritePermission(socket));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not a write permission error, but is a read permission error.

this wrong error was already here before your pr, but now is a good time to fix it

let req = Request::builder()
.method(Method::GET)
.uri(uri)
.body(Either::Right(Empty::<Bytes>::new()))
.map_err(|err| anyhow!("Error building the request. Reason: {}", err))?;
.map_err(EngineClientError::HttpError)?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably HttpError is not a good error here, it seems more a request builder error


let buf = res.collect().await?.aggregate();
let buf = res
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed that before parsing the json there is no check on the status like

        let status = res.status();

        match status {
            StatusCode::OK => Ok(()),
            _ => {
            // ...
            }

same as before now is a good time to fix it


let res = self
.client
.request(req)
.await
.map_err(|err| anyhow!("Error during the http request: reason {}", err))?;
.map_err(|err| EngineClientError::HyperRequest(err.to_string()))?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably is better an enum variant with a #[from] for "hyper errors" (I don't remember the exact type) instead of manually crafting a string

Comment on lines 236 to 240
let (ws_stream, _) = client_async("ws://localhost/monitor", stream)
.await
.map_err(|err| {
EngineClientError::WebSocketError(format!("WebSocket initialization failed: {}", err))
})?;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here probably we should use a variant with #[from] tokio_tungstenite::tungstenite::Error.

you can move the unused WebsocketError::ConnectionError from WebsocketError to EngineClientErrot

@zedddie16
Copy link
Author

i hope i did requested changes well :p

Comment on lines +126 to +134
impl EngineClientError {
pub fn request_builder_error(err: http::Error) -> Self {
Self::RequestBuilderError(err)
}

pub fn hyper_request_error<E: std::fmt::Display>(err: E) -> Self {
Self::HyperRequestError(err.to_string())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need this functions

Comment on lines +147 to +149
/// Connection error
#[error("WebSocket connection error: {0}")]
ConnectionError(String),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is not used anymore right? you moved to EngineClientError

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is used in line 260 client.rs events_stream when I delete it from errors it leads to more problems in event stream error handling

Comment on lines +120 to +124
impl From<io::Error> for EngineClientError {
fn from(err: io::Error) -> Self {
Self::IoError(err.to_string())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be implemented as a #from with thiserror

Comment on lines +114 to +118
impl From<std::str::Utf8Error> for EngineClientError {
fn from(err: std::str::Utf8Error) -> Self {
Self::Utf8Error(err.to_string())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this can be implemented as a #from with thiserror

Comment on lines +89 to +93
impl From<std::ffi::NulError> for EngineClientError {
fn from(err: std::ffi::NulError) -> Self {
Self::CStringConversion(err.to_string())
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure if this trait impl is necessary

Comment on lines +56 to +58
/// HTTP error
#[error("HTTP error: {0}")]
HttpError(#[from] http::Error),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be removed right?

Comment on lines +44 to +50
/// Hyper client error
#[error("Hyper client error: {0}")]
HyperClientError(#[from] HyperError),

/// Hyper request error
#[error("Hyper request error: {0}")]
HyperRequestError(String),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to merge this 2?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but I thought HyperClientError and HyperRequestError are different, I can just do one HyperError then instead of these two

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

seems like not, HyperClientError is for requesting from client

let res = self
            .client
            .request(req)
            .await
            .map_err(EngineClientError::HyperError)?;

while HyperRequestError is for collecting value from request, so I am not sure I can merge them into one, because they are different types actually

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so there is will be RequestBuilderError for builders;
CollectResponseError for collecting response;
and HyperError for error during sending constructed request.
is it okay?

@zedddie16
Copy link
Author

I removed everything you said, but have a problem, as I said earlier on line 260 events_stream is returning <Event, WebsocketError> so we cant convert Error to WebsocketError when there is no WebSocketConnectionError(which represent any WebsocketError (tokio_tungstenite::tungstenite::Error)) in our WebsocketError

@zedddie16
Copy link
Author

to delete WebsocketConnectionError I will need to change fn event_monitor to return Result<Event, EngineClientError>>, EngineClientError> instead of Result<Event, WebsocketError>>, EngineClientError>, which seems to be strange, and will require to move all WebsocketError enums into EngineClientError

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants