use std::fmt::{self, Debug, Display, Formatter};
use async_trait::async_trait;
use serde::Serialize;
use super::{Scribe, try_set_header};
use crate::http::header::{CONTENT_TYPE, HeaderValue};
use crate::http::{Response, StatusError};
pub struct Json<T>(pub T);
#[async_trait]
impl<T> Scribe for Json<T>
where
T: Serialize + Send,
{
fn render(self, res: &mut Response) {
match serde_json::to_vec(&self.0) {
Ok(bytes) => {
try_set_header(
&mut res.headers,
CONTENT_TYPE,
HeaderValue::from_static("application/json; charset=utf-8"),
);
let _ = res.write_body(bytes);
}
Err(e) => {
tracing::error!(error = ?e, "JsonContent write error");
res.render(StatusError::internal_server_error());
}
}
}
}
impl<T: Debug> Debug for Json<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("Json").field(&self.0).finish()
}
}
impl<T: Display> Display for Json<T> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prelude::*;
use crate::test::{ResponseExt, TestClient};
#[tokio::test]
async fn test_write_json_content() {
#[derive(Serialize, Debug)]
struct User {
name: String,
}
#[handler]
async fn test() -> Json<User> {
Json(User {
name: "jobs".into(),
})
}
let router = Router::new().push(Router::with_path("test").get(test));
let mut res = TestClient::get("http://127.0.0.1:8698/test")
.send(router)
.await;
assert_eq!(res.take_string().await.unwrap(), r#"{"name":"jobs"}"#);
assert_eq!(
res.headers().get("content-type").unwrap(),
"application/json; charset=utf-8"
);
}
}