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