Search code examples
rustrust-axum

is there any way to handle nested ok_or() in rust?


I have a function that returns a struct AppError wrapped with Result when I want to throw an error.

Below I am trying to throw 401(Unauthorized) with the message "Invalid Credentials" but every time I unwrap the code I have to write .ok_or() to handle this.

pub async fn refresh_uses_session(
    State(app_state): State<AppState>,
    cookie_jar: CookieJar,
) -> Result<Response, AppError> {
    let refresh_token_cookie = cookie_jar
        .get("refresh_token")
        .ok_or((StatusCode::UNAUTHORIZED, "Invalid Credentials").into_app_error())?;

    let cookie_expiration_time = refresh_token_cookie
        .expires()
        .ok_or((StatusCode::UNAUTHORIZED, "Invalid Credentials").into_app_error())?
        .datetime()
        .ok_or((StatusCode::UNAUTHORIZED, "Invalid Credentials").into_app_error())?;

    if cookie_expiration_time <= OffsetDateTime::now_utc() {
        return Err((StatusCode::UNAUTHORIZED, "Invalid Credentials").into_app_error());
    }

    //…
}

Is there any way to reduce the .ok_or() calls without changing the return type or helper function?


Solution

  • Using the Option API you should be able to use the and_then and filter functions to keep rolling your optional chain into a single value.

    The following (much simplified) example should illustrate what I mean:

    struct Cookie {
        expires: Option<String>,
    }
    
    struct CookieJar {
        refresh_token: Option<Cookie>,
    }
    
    fn main() {
        let result = test_function();
        println!("{:?}", result);
    }
    
    
    fn test_function() -> Result<String, String> {
        let cookie_jar = CookieJar { refresh_token: Some(Cookie { expires: None }) };
    
        cookie_jar
            .refresh_token
            .and_then(|cookie| {cookie.expires})
            .filter(|expiration| {expiration == "2024-04-28"})
            .ok_or("My error message".to_string())
    }
    

    Playground