ironshield_types/
request.rs

1use serde::{Deserialize, Serialize};
2
3/// * `endpoint`:  The endpoint URL for the request.
4/// * `timestamp`: The timestamp of the request in unix millis.
5#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct IronShieldRequest {
8    pub endpoint:  String,
9    pub timestamp: i64,
10}
11
12impl IronShieldRequest {
13    /// Constructor for creating a new IronShieldRequest instance.
14    pub fn new(
15        endpoint:  String,
16        timestamp: i64
17    ) -> Self {
18        Self { 
19            endpoint,
20            timestamp,
21        }
22    }
23    
24    /// Concatenates the request data into a string.
25    /// 
26    /// Concatenates:
27    /// - `endpoint`:  as a string.
28    /// - `timestamp`: as a string.
29    pub fn concat_struct(&self) -> String {
30        format!(
31            "{}|{}",
32            self.endpoint,
33            self.timestamp
34        )
35    }
36    
37    /// Creates an `IronShieldRequest` from a concatenated string.
38    /// 
39    /// This function reverses the operation of
40    /// `IronShieldRequest::concat_struct`.
41    /// Expects a string in the format: "endpoint|timestamp".
42    /// 
43    /// # Arguments
44    /// * `concat_string`: The concatenated string to parse, typically
45    ///                    generated by `concat_struct()`.
46    /// 
47    /// # Returns
48    /// * `Result<Self, String>`: A result containing the parsed
49    ///                           `IronShieldRequest`
50    ///                           or an error message if parsing fails.
51    pub fn from_concat_struct(concat_string: &str) -> Result<Self, String> {
52        let parts: Vec<&str> = concat_string.split('|').collect();
53
54        if parts.len() != 2 {
55            return Err("Invalid format, expected 'endpoint|timestamp'".to_string());
56        }
57
58        let endpoint = parts[0].to_string();
59        let timestamp = parts[1].parse::<i64>()
60            .map_err(|_| "Failed to parse timestamp".to_string())?;
61
62        Ok(Self { endpoint, timestamp })
63    }
64
65    /// Encodes the response as a base64url string for HTTP header transport.
66    ///
67    /// This method concatenates all response fields using the established `|` delimiter
68    /// format, and then base64url-encodes the result for safe transport in HTTP headers.
69    ///
70    /// # Returns
71    /// * `String`: Base64url-encoded string ready for HTTP header use
72    ///
73    /// # Example
74    /// ```
75    /// use ironshield_types::IronShieldRequest;
76    /// let request = IronShieldRequest::new("https://example.com/api".to_string(), 123456789);
77    /// let header_value = request.to_base64url_header();
78    /// ```
79    pub fn to_base64url_header(&self) -> String {
80        crate::serde_utils::concat_struct_base64url_encode(&self.concat_struct())
81    }
82
83    /// Decodes a base64url-encoded response from an HTTP header.
84    ///
85    /// This method reverses the `to_base64url_header()` operation by first base64url-decoding
86    /// the input string and then parsing it using the established `|` delimiter format.
87    ///
88    /// # Arguments
89    /// * `encoded_header`: The base64url-encoded string from the HTTP header.
90    ///
91    /// # Returns
92    /// * `Result<Self, String>`: Decoded response or detailed error message.
93    ///
94    /// # Example
95    /// ```
96    /// use ironshield_types::IronShieldRequest;
97    /// // Create a response and encode it.
98    /// let original = IronShieldRequest::new("https://example.com/api".to_string(), 123456789);
99    /// let header_value = original.to_base64url_header();
100    /// // Decode it back.
101    /// let decoded = IronShieldRequest::from_base64url_header(&header_value).unwrap();
102    /// assert_eq!(original.endpoint, decoded.endpoint);
103    /// ```
104    pub fn from_base64url_header(encoded_header: &str) -> Result<Self, String> {
105        // Decode using the existing serde_utils function.
106        let concat_str: String = crate::serde_utils::concat_struct_base64url_decode(encoded_header.to_string())?;
107        // Parse using the existing concat_struct format.
108        Self::from_concat_struct(&concat_str)
109    }
110}