1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
use bytes::Bytes;
use http::HeaderMap;
use http_body_util::BodyExt;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::server::{NgynContext, NgynResponse, Transformer};
#[derive(Serialize, Deserialize)]
/// Responses are hard to manage, especially when they are not standardized.
/// This is why Ngyn, by default, provides a json response format.
///
/// The json response format is a JSON object with two keys: `data` and `error`.
/// This would ideally make your responses more predictable and easier to manage.
/// A valid response would look like:
/// ```json
/// {
/// "data": {
/// "key": "value"
/// },
/// "error": null
/// }
/// ```
/// A valid error response would look like:
/// ```json
/// {
/// "data": null,
/// "error": {
/// "status": 404,
/// "message": "Not Found"
/// }
/// }
/// ```
/// The `data` key is used to store the response data, while the `error` key is used to store error data.
/// Both keys are optional, but at least one of them should be present.
///
///
/// ### How to create a json response?
/// Ngyn provides an implementation on [`JsonResult`] to convert it to a json response.
/// This means anytime you make use of a `JsonResult` in your controlled routes, it will be converted to a json response.
///
/// #### Example
/// ```rust ignore
/// use ngyn::prelude::*;
///
/// #[controller]
/// struct MyController;
///
/// #[routes]
/// impl MyController {
/// #[get("/")]
/// async fn get(&self, cx: &mut NgynContext) -> Result<Vec<u8>, ()> {
/// let data = vec![1, 2, 3];
/// Ok(data)
/// }
/// }
/// ```
pub struct JsonResponse<D: Serialize, E: Serialize> {
data: Option<D>,
error: Option<E>,
}
impl<D: Serialize, E: Serialize> JsonResponse<D, E> {
/// Creates a new json response.
pub fn new(data: Option<D>, error: Option<E>) -> Self {
Self { data, error }
}
/// Returns the data.
pub fn data(&self) -> Option<&D> {
self.data.as_ref()
}
/// Returns the error data.
pub fn error(&self) -> Option<&E> {
self.error.as_ref()
}
}
/// A shorthand for a json result.
///
/// This is useful when you want to return a json response.
/// It is a type alias for a [`Result`] with a [`Value`] as the `ok` and `error` type.
///
/// ### Example
///
/// ```rust ignore
/// use ngyn::prelude::*;
///
/// #[controller]
/// struct MyController;
///
/// #[routes]
/// impl MyController {
/// #[get("/")]
/// async fn get(&self, cx: &mut NgynContext) -> JsonResult {
/// let data = json!({ "key": "value" });
/// Ok(data)
/// }
/// }
/// ```
pub type JsonResult = Result<Value, Value>;
impl<'a> Transformer<'a> for &'a NgynResponse {
fn transform(_cx: &'a mut NgynContext, res: &'a mut NgynResponse) -> Self {
res
}
}
impl<'a> Transformer<'a> for &'a mut NgynResponse {
fn transform(_cx: &'a mut NgynContext, res: &'a mut NgynResponse) -> Self {
res
}
}
/// A shorthand for transforming a `HeaderMap` reference.
///
/// This is useful when you need to access the headers of a response.
impl<'a> Transformer<'a> for &'a HeaderMap {
fn transform(_cx: &'a mut NgynContext, res: &'a mut NgynResponse) -> Self {
res.headers()
}
}
/// A shorthand for transforming a mutable `HeaderMap` reference.
///
/// This is useful when you want to add or remove headers from a response.
impl<'a> Transformer<'a> for &'a mut HeaderMap {
fn transform(_cx: &'a mut NgynContext, res: &'a mut NgynResponse) -> Self {
res.headers_mut()
}
}
pub trait PeekBytes {
#[allow(async_fn_in_trait)]
/// Peeks the bytes of a valid ngyn response body.
///
/// You can use this to read the bytes of a response body without consuming it(Well, we make it look like we don't).
async fn peek_bytes(&mut self, f: impl FnMut(&Bytes));
}
impl PeekBytes for NgynResponse {
async fn peek_bytes(&mut self, mut f: impl FnMut(&Bytes)) {
let frame = self.frame().await;
if let Some(Ok(frame)) = frame {
if let Ok(bytes) = frame.into_data() {
f(&bytes);
// body has been read, so we need to set it back
*self.body_mut() = bytes.into();
}
}
}
}