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