Skip to content

Commit 23e6591

Browse files
Add impl OperationOutput for axum-extra Attachment (#276)
1 parent 50e17b8 commit 23e6591

File tree

2 files changed

+80
-2
lines changed

2 files changed

+80
-2
lines changed

crates/aide/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ axum-tokio = ["axum", "axum/tokio"]
4747
axum-ws = ["axum", "axum/ws"]
4848

4949
axum-extra = ["axum", "dep:axum-extra"]
50+
axum-extra-attachment = ["axum-extra", "axum-extra/attachment"]
5051
axum-extra-cached = ["axum-extra", "axum-extra/cached"]
5152
axum-extra-cookie = ["axum-extra", "axum-extra/cookie"]
5253
axum-extra-cookie-private = ["axum-extra", "axum-extra/cookie-private"]

crates/aide/src/axum/outputs.rs

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,16 @@ impl OperationOutput for Redirect {
255255
#[cfg(feature = "axum-extra")]
256256
#[allow(unused_imports)]
257257
mod extra {
258-
use axum_extra::extract;
258+
use axum_extra::{extract, response};
259259

260260
use super::*;
261-
use crate::operation::OperationOutput;
261+
use crate::{
262+
openapi::{
263+
Header, HeaderStyle, MediaType, ParameterSchemaOrContent, ReferenceOr, Response,
264+
SchemaObject,
265+
},
266+
operation::OperationOutput,
267+
};
262268

263269
#[cfg(feature = "axum-extra-cookie")]
264270
impl OperationOutput for extract::CookieJar {
@@ -269,4 +275,75 @@ mod extra {
269275
impl OperationOutput for extract::PrivateCookieJar {
270276
type Inner = ();
271277
}
278+
279+
#[cfg(feature = "axum-extra-attachment")]
280+
impl<T> OperationOutput for response::Attachment<T> {
281+
type Inner = Self;
282+
283+
fn operation_response(
284+
_ctx: &mut GenContext,
285+
_operation: &mut Operation,
286+
) -> Option<Response> {
287+
Some(Response {
288+
description: "File download".to_owned(),
289+
headers: [
290+
(
291+
"Content-Disposition".to_owned(),
292+
ReferenceOr::Item(Header {
293+
description: Some(
294+
"Controls download behavior. Use `attachment` to prompt save dialog, `inline` to display in browser. Includes suggested filename."
295+
.to_owned(),
296+
),
297+
required: false,
298+
format: ParameterSchemaOrContent::Schema(SchemaObject {
299+
json_schema: json_schema!({
300+
"type": "string",
301+
}),
302+
example: None,
303+
external_docs: None,
304+
}),
305+
extensions: Default::default(),
306+
deprecated: None,
307+
example: Some(serde_json::json!(r#"attachment; filename="xyz.pdf""#)),
308+
examples: IndexMap::default(),
309+
style: HeaderStyle::Simple,
310+
}),
311+
),
312+
(
313+
"Content-Type".to_owned(),
314+
ReferenceOr::Item(Header {
315+
description: Some("MIME type of the file".to_owned()),
316+
required: false,
317+
format: ParameterSchemaOrContent::Schema(SchemaObject {
318+
json_schema: json_schema!({
319+
"type": "string",
320+
}),
321+
external_docs: None,
322+
example: None,
323+
}),
324+
extensions: Default::default(),
325+
deprecated: None,
326+
example: Some(serde_json::json!("application/pdf")),
327+
examples: IndexMap::default(),
328+
style: HeaderStyle::Simple,
329+
}),
330+
),
331+
]
332+
.into_iter()
333+
.collect(),
334+
content: [("application/octet-stream".to_owned(), MediaType::default())] .into_iter()
335+
.collect(),
336+
..Default::default()
337+
})
338+
}
339+
340+
fn inferred_responses(
341+
ctx: &mut GenContext,
342+
operation: &mut Operation,
343+
) -> Vec<(Option<StatusCode>, Response)> {
344+
Self::operation_response(ctx, operation)
345+
.map(|r| vec![(Some(StatusCode::Code(200)), r)])
346+
.unwrap_or_default()
347+
}
348+
}
272349
}

0 commit comments

Comments
 (0)