foxtive-ntex 0.31.0

Micro-Framework Based on Ntex
Documentation
use crate::error::HttpError;
use crate::{FOXTIVE_NTEX, FoxtiveNtexExt};
use foxtive::prelude::AppMessage;
use ntex::http::Payload;
use ntex::util::BytesMut;
use ntex::web::{FromRequest, HttpRequest};
use serde::de::DeserializeOwned;
use std::ops;

pub struct JsonBody<T: DeserializeOwned>(T);

impl<T: DeserializeOwned> JsonBody<T> {
    pub fn into_inner(self) -> T {
        self.0
    }
}

impl<T: DeserializeOwned> ops::Deref for JsonBody<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.0
    }
}

impl<T: DeserializeOwned> ops::DerefMut for JsonBody<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.0
    }
}

impl<T: DeserializeOwned, Err> FromRequest<Err> for JsonBody<T> {
    type Error = HttpError;

    async fn from_request(
        _req: &HttpRequest,
        payload: &mut Payload,
    ) -> Result<JsonBody<T>, Self::Error> {
        let max_size = FOXTIVE_NTEX.app().body_config.json_limit;
        let mut bytes = BytesMut::new();
        let mut total_size = 0;

        while let Some(item) = ntex::util::stream_recv(payload).await {
            let chunk = item?;
            total_size += chunk.len();

            if total_size > max_size {
                return Err(HttpError::AppMessage(AppMessage::invalid(
                    "JSON body exceeded maximum size",
                )));
            }

            bytes.extend_from_slice(&chunk);
        }

        let json = String::from_utf8(bytes.to_vec())?;
        let value = serde_json::from_str::<T>(&json)
            .map_err(|e| HttpError::AppMessage(AppMessage::invalid(e.to_string())))?;

        Ok(JsonBody(value))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde::{Deserialize, Serialize};

    #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
    struct TestStruct {
        field1: String,
        field2: i32,
    }

    #[test]
    fn test_deref() {
        let value = TestStruct {
            field1: "value1".to_string(),
            field2: 42,
        };
        let json_body = JsonBody(value.clone());

        assert_eq!(*json_body, value);
        assert_eq!(json_body.field1, "value1");
        assert_eq!(json_body.field2, 42);
    }

    #[test]
    fn test_deref_mut() {
        let value = TestStruct {
            field1: "value1".to_string(),
            field2: 42,
        };
        let mut json_body = JsonBody(value);

        json_body.field2 = 100;
        assert_eq!(json_body.field2, 100);
    }

    #[test]
    fn test_into_inner() {
        let value = TestStruct {
            field1: "value1".to_string(),
            field2: 42,
        };
        let json_body = JsonBody(value.clone());

        let extracted = json_body.into_inner();
        assert_eq!(extracted, value);
    }
}