use axum::body::Bytes;
use axum::extract::FromRequest;
use axum::extract::Request;
use axum::http::StatusCode;
use axum::http::header::{CONTENT_TYPE, HeaderMap, HeaderValue};
use serde::de::DeserializeOwned;
use super::AppState;
pub async fn mirror_yaml(
state: &AppState,
bucket: &str,
id: &str,
yaml: &str,
) -> anyhow::Result<()> {
let kv = state.jetstream.get_key_value(bucket).await?;
kv.put(id, yaml.to_owned().into_bytes().into()).await?;
Ok(())
}
pub fn yaml_headers() -> HeaderMap {
let mut h = HeaderMap::new();
h.insert(CONTENT_TYPE, HeaderValue::from_static("application/yaml"));
h
}
pub struct YamlOrJson<T> {
pub value: T,
pub raw_yaml: Option<String>,
}
impl<S, T> FromRequest<S> for YamlOrJson<T>
where
S: Send + Sync,
T: DeserializeOwned,
{
type Rejection = (StatusCode, String);
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
let is_yaml = req
.headers()
.get(CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.map(|ct| {
let head = ct.split(';').next().unwrap_or("").trim();
matches!(
head,
"application/yaml" | "text/yaml" | "application/x-yaml" | "text/x-yaml"
)
})
.unwrap_or(false);
let bytes = Bytes::from_request(req, state)
.await
.map_err(|e| (StatusCode::BAD_REQUEST, format!("read request body: {e}")))?;
if is_yaml {
let raw = String::from_utf8(bytes.to_vec()).map_err(|e| {
(
StatusCode::BAD_REQUEST,
format!("YAML body must be UTF-8: {e}"),
)
})?;
let value: T = serde_yaml::from_str(&raw)
.map_err(|e| (StatusCode::BAD_REQUEST, format!("parse YAML body: {e}")))?;
Ok(Self {
value,
raw_yaml: Some(raw),
})
} else {
let value: T = serde_json::from_slice(&bytes)
.map_err(|e| (StatusCode::BAD_REQUEST, format!("parse JSON body: {e}")))?;
Ok(Self {
value,
raw_yaml: None,
})
}
}
}