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}