use std::fmt::Write;
use poem::{http::header::CONTENT_DISPOSITION, Body, IntoResponse, Response};
use crate::{
payload::{Binary, Payload},
registry::{MetaHeader, MetaMediaType, MetaResponse, MetaResponses, MetaSchemaRef, Registry},
types::Type,
ApiResponse,
};
const CONTENT_DISPOSITION_DESC: &str = "Indicate if the content is expected to be displayed inline in the browser, that is, as a Web page or as part of a Web page, or as an attachment, that is downloaded and saved locally.";
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum AttachmentType {
Inline,
Attachment,
}
impl AttachmentType {
#[inline]
fn as_str(&self) -> &'static str {
match self {
AttachmentType::Inline => "inline",
AttachmentType::Attachment => "attachment",
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Attachment<T> {
data: Binary<T>,
ty: AttachmentType,
filename: Option<String>,
}
impl<T: Into<Body> + Send> Attachment<T> {
pub fn new(data: T) -> Self {
Self {
data: Binary(data),
ty: AttachmentType::Attachment,
filename: None,
}
}
#[must_use]
pub fn attachment_type(self, ty: AttachmentType) -> Self {
Self { ty, ..self }
}
#[must_use]
pub fn filename(self, filename: impl Into<String>) -> Self {
Self {
filename: Some(filename.into()),
..self
}
}
fn content_disposition(&self) -> String {
let mut content_disposition = self.ty.as_str().to_string();
if let Some(legal_filename) = self.filename.as_ref().map(|filename| {
filename
.replace('\\', "\\\\")
.replace('\"', "\\\"")
.replace('\r', "\\\r")
.replace('\n', "\\\n")
}) {
_ = write!(content_disposition, "; filename=\"{legal_filename}\"");
}
content_disposition
}
}
impl<T: Into<Body> + Send> Payload for Attachment<T> {
const CONTENT_TYPE: &'static str = Binary::<T>::CONTENT_TYPE;
fn schema_ref() -> MetaSchemaRef {
Binary::<T>::schema_ref()
}
}
impl<T: Into<Body> + Send> IntoResponse for Attachment<T> {
fn into_response(self) -> Response {
let content_disposition = self.content_disposition();
self.data
.with_header(CONTENT_DISPOSITION, content_disposition)
.into_response()
}
}
impl<T: Into<Body> + Send> ApiResponse for Attachment<T> {
fn meta() -> MetaResponses {
MetaResponses {
responses: vec![MetaResponse {
description: "",
status: Some(200),
content: vec![MetaMediaType {
content_type: Self::CONTENT_TYPE,
schema: Self::schema_ref(),
}],
headers: vec![MetaHeader {
name: "Content-Disposition".to_string(),
description: Some(CONTENT_DISPOSITION_DESC.to_string()),
required: true,
deprecated: false,
schema: String::schema_ref(),
}],
}],
}
}
fn register(_registry: &mut Registry) {}
}