Not necessarily, I just wanted to understand your program a bit better. That is where I've ended up in my own code, but that's due to factors that might not translate to yours.
I'm not quite sure what you mean by your initial question?
The point of an Axum handler method's return value is to generate an HTTP response, including its status code, headers, and body. The rust representation of the response is your application's means to that single end. If you are returning (status, None) from a handler method, what HTTP response do you expect your service to send to the client?
The reason I ask is that Axum can only route to functions that return IntoResponse types. Axum has a fairly large collection of implementations, including an implementation of IntoResponse for tuple types of the form (T1, T2), so long as T1 and T2 both implement IntoResponse themselves so long as T2 implements IntoResponse and T1 implements IntoResponseParts. However, there is no implementation of IntoResponse for Option.
In order for your program to work, you'll need to decide how you want your Option<Json<Vehicle>> to be represented as an HTTP response, and then implement that. For coherence reasons, you'll likely have to wrap the whole mess in a new type, and at that point it might be easier to define your own option-like response type, instead.
Putting that all together gives this:
use axum::{
Json,
http::StatusCode,
response::{IntoResponse, Response},
};
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct Vehicle {
manufacturer: String,
model: String,
year: u32,
id: Option<String>,
}
async fn item_get() -> (StatusCode, JsonOption<Vehicle>) {
(StatusCode::OK, JsonOption::None)
}
enum JsonOption<T> {
Some(T),
None,
}
impl<T> IntoResponse for JsonOption<T>
where
Json<T>: IntoResponse,
{
fn into_response(self) -> Response {
match self {
Self::Some(val) => Json(val).into_response(),
Self::None => StatusCode::NO_CONTENT.into_response(),
}
}
}
You can also pull the logic into your handler function, if you prefer. Response implements IntoResponse trivially, so you can write handlers like this:
async fn item_get() -> Response {
let vehicle: Option<Vehicle> = todo!();
match vehicle {
Some(v) => (StatusCode::OK, Json(vehicle)).into_response(),
None => StatusCode::NOT_FOUND.into_response(),
}
}
I don't do this because it makes it harder for tests to inspect application-specific characteristics of the response, but it works just fine and if you're not testing handlers directly you might not be concerned about that.