aide icon indicating copy to clipboard operation
aide copied to clipboard

OperationOutput derive macro

Open baxterjo opened this issue 1 month ago • 0 comments

Partially solves: https://github.com/tamasfe/aide/issues/227

Added my first pass at an operation output derive macro. This macro allows users to derive OperationOutput for an enum with single field tuple variants in which the field type already implements OperationOutput.

Example Usage

This is taken straight from a test in the PR

use aide::axum::IntoApiResponse;
use aide::{derive::OperationOutput, OperationIo};
use axum::{body::Body, response::IntoResponse};
use http::StatusCode;
use schemars::JsonSchema;
use serde::Serialize;

pub const INTERNAL_SERVER_ERROR: u16 = 500;

// Using the simple implementation of OperationOutput here for brevity.
#[derive(Debug, Serialize, JsonSchema, OperationIo)]
#[aide(output)]
pub struct MyResponse {
    pub success: bool,
}

#[derive(Debug, Serialize, OperationOutput)]
pub enum MyMultiResponse {
    // Use a literal status code.
    #[aide(status_code = 200)]
    Success(MyResponse),
    // Use any const status code. Hint: This includes
    // http::StatusCode::as_u16()
    #[aide(status_code=INTERNAL_SERVER_ERROR)]
    Failure(MyResponse),
    // Use an existing implementation's default status code.
    DefaultStatusCode(String),
    // Override an existing implementation's status code.
    #[aide(status_code = 201)]
    DefaultStatusCodeOverride(String),
    // Use no status code.
    NoStatusCode(MyResponse),
}

/// This implementation is required for the blanket implementation of [`IntoApiResponse`]
/// however it is not within the perview of this macro, so we will keep it short. (And
/// incorrect)
impl IntoResponse for MyMultiResponse {
    fn into_response(self) -> axum::response::Response {
        (StatusCode::OK, Body::from("Success")).into_response()
    }
}

pub async fn my_handler() -> impl IntoApiResponse {
    MyMultiResponse::Success(MyResponse { success: true })
}

There are some structural things I would like to change. Like serde and schemars I would prefer the trait OperationOutput be namespaced behind a mod, so that way the derive macro can have the same name. Right now I have the reverse implementation. Which makes sense for a macro that has such a narrow implementation.

I added this because I need it, I think in the future it would make sense to add more derive features for different container types.

For example:

#[derive(Debug, Serialize, JsonSchema, OperationOutput)]
// Require a content_type attribute. And an optional status_code attribute
// This content type would introduce trait bounds of Serialize and JsonSchema
#[aide(content_type = "application/json", status_code = 200)] 
pub struct MyResponse {
  pub success: bool
}

baxterjo avatar Nov 17 '25 22:11 baxterjo