Skip to content

Result handling #73

Description

@lucasavila00

I would like if qubit handled Results for me.

I'm using a database for the queries and every operation returns a Result.

I would like if qubit:

  • Did not send the Err contents to the client-side
  • Had a compile time setting that forced every handler to return a QubitResult
  • Had an option on the client to throw on Errs, generates types for the successful Ok only

For now I'm using a custom result type:

use std::error::Error;
use std::sync::Arc;

use serde::Serialize;
use ts_rs::TS;
#[derive(Debug, Clone)]
pub enum RpcErrorMessage {
    Anyhow(Arc<anyhow::Error>),
    Public(String),
}

impl Serialize for RpcErrorMessage {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let msg = match self {
            RpcErrorMessage::Anyhow(e) => {
                dbg!(e);
                "Internal Server Error"
            }
            RpcErrorMessage::Public(s) => s,
        };
        serializer.serialize_str(msg)
    }
}

#[derive(Debug, Clone, Serialize, TS)]
pub struct RpcError {
    #[ts(as = "String")]
    message: RpcErrorMessage,
}

fn app_anyhow<T: Error + Send + Sync + 'static>(it: T) -> RpcError {
    RpcError {
        message: RpcErrorMessage::Anyhow(Arc::new(anyhow::Error::new(it))),
    }
}

impl<T: Error + Send + Sync + 'static> From<T> for RpcError {
    fn from(value: T) -> Self {
        app_anyhow(value)
    }
}
pub fn public_err<T>(msg: &str) -> Result<T, RpcError> {
    Err(RpcError {
        message: RpcErrorMessage::Public(msg.to_string()),
    })
}

pub type RpcResult<T> = Result<T, RpcError>;

Usage:

#[handler(query)]
async fn read_posts(ctx: AuthCtx) -> RpcResult<Vec<Post>> {
    use crate::schema::posts::dsl::*;
    let results = posts
        .limit(5)
        .select(Post::as_select())
        .load(&mut ctx.pg_pool.get()?)?;

    Ok(results)
}

And an unwrap function in TS

type AnyResult = { Ok: any } | { Err: { message: string } };

type ExtractOk<T extends AnyResult> = T extends { Ok: infer U } ? U : never;

export const Unwrap = <T extends AnyResult>(x: T): ExtractOk<T> => {
    if ("Ok" in x) {
        return x.Ok;
    }
    if ("Err" in x) {
        if ("message" in x.Err) {
            throw new Error(x.Err.message);
        }
    }
    console.log(x);
    throw new Error("Unwrap failed: " + String(x));
};

Thanks!

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions