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}