Skip to content

More idiomatic and ergonomic error handling #36

@d-k-bo

Description

@d-k-bo

Currently, all client methods return Result<T, LemmyErrorType>. The LemmyErrorType error type is suboptimal for users of the library for multiple reasons:

  • it doesn't implement std::error::Error (although this could be fixed in a one-line PR to lemmy_utils), so it can't be used with eyre/anyhow and anything else that expects an impl Error or dyn Error without wrapping it in another error type
  • it doesn't differentiate between HTTP errors and errors that are returned from the lemmy API
  • in the case of HTTP errors, the original error isn't preserved (e.g. reqwest::Error)
Executable Example
#!/usr/bin/env -S cargo +nightly -Zscript
---
package.edition = "2024"

[dependencies]
lemmy-client = "1.0.5"
tokio = { version = "1.45.0", features = ["macros", "rt-multi-thread"] }
---

use lemmy_client::{
    ClientOptions, LemmyClient,
    lemmy_api_common::{
        lemmy_db_schema::newtypes::PostId,
        post::{GetPost, GetPosts},
    },
};

#[tokio::main]
async fn main() {
    http_error().await;
    lemmy_error().await;
}

async fn http_error() {
    let client = LemmyClient::new(ClientOptions {
        domain: "nolemmy.example.org".to_owned(),
        secure: true,
    });

    if let Err(e) = client.list_posts(GetPosts::default()).await {
        // Unknown("error sending request for url (https://nolemmy.example.org/api/v3/post/list)")
        println!("{e:?}");
    }
}

async fn lemmy_error() {
    let client = LemmyClient::new(ClientOptions {
        domain: "lemm.ee".to_owned(),
        secure: true,
    });

    if let Err(e) = client
        .get_post(GetPost {
            id: Some(PostId(123456789)),
            comment_id: None,
        })
        .await
    {
        // CouldntFindPost
        println!("{e:?}");
    }
}

I suggest that this crate returns a different error type instead that implements std::error::Error, provides access to an underlying error at least via Error::source() and at least provides some methods to check if the error is an HTTP error or an error returned by the API.

This could also be done using the widespread “error enum” pattern (which can be implemented using thiserror, but can also easily be written by hand).

Error Enum Example
#[derive(Debug)]
pub enum Error {
    #[cfg(target_family = "wasm")]
    Http(gloo::net::Error),
    #[cfg(not(target_family = "wasm"))]
    Http(reqwest::Error),
    Lemmy(LemmyErrorType)
}

P. S.: I think a lot could be implemented easier if this crate used reqwest for both WASM and non-WASM, although I don't know the tradeoffs between gloo::net and reqwest.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions