1use serde_json::{Value, json};
2
3use crate::{BpiError, BpiResult};
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct WalletInfoParams {
8 platform_type: u32,
9 timestamp_ms: i64,
10 trace_id: i64,
11 version: String,
12}
13
14impl WalletInfoParams {
15 pub fn new() -> Self {
17 Self::at_timestamp(chrono::Utc::now().timestamp_millis())
18 }
19
20 pub fn at_timestamp(timestamp_ms: i64) -> Self {
22 Self {
23 platform_type: 3,
24 timestamp_ms,
25 trace_id: timestamp_ms,
26 version: "1.0".to_string(),
27 }
28 }
29
30 pub fn with_platform_type(mut self, platform_type: u32) -> BpiResult<Self> {
31 if platform_type == 0 {
32 return Err(BpiError::invalid_parameter(
33 "platformType",
34 "platform type must be non-zero",
35 ));
36 }
37
38 self.platform_type = platform_type;
39 Ok(self)
40 }
41
42 pub fn with_trace_id(mut self, trace_id: i64) -> BpiResult<Self> {
43 if trace_id <= 0 {
44 return Err(BpiError::invalid_parameter(
45 "traceId",
46 "trace id must be positive",
47 ));
48 }
49
50 self.trace_id = trace_id;
51 Ok(self)
52 }
53
54 pub fn with_version(mut self, version: impl Into<String>) -> BpiResult<Self> {
55 let version = version.into();
56 let version = version.trim();
57 if version.is_empty() {
58 return Err(BpiError::invalid_parameter(
59 "version",
60 "version cannot be blank",
61 ));
62 }
63
64 self.version = version.to_string();
65 Ok(self)
66 }
67
68 pub(crate) fn body(&self, csrf: &str) -> Value {
69 json!({
70 "csrf": csrf,
71 "platformType": self.platform_type,
72 "timestamp": self.timestamp_ms,
73 "traceId": self.trace_id,
74 "version": self.version,
75 })
76 }
77}
78
79impl Default for WalletInfoParams {
80 fn default() -> Self {
81 Self::new()
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn wallet_info_params_serializes_default_body() {
91 let params = WalletInfoParams::at_timestamp(1_700_000_000_000);
92
93 assert_eq!(
94 params.body("csrf-token"),
95 json!({
96 "csrf": "csrf-token",
97 "platformType": 3,
98 "timestamp": 1_700_000_000_000_i64,
99 "traceId": 1_700_000_000_000_i64,
100 "version": "1.0",
101 })
102 );
103 }
104
105 #[test]
106 fn wallet_info_params_serializes_custom_values() -> BpiResult<()> {
107 let params = WalletInfoParams::at_timestamp(1_700_000_000_000)
108 .with_platform_type(4)?
109 .with_trace_id(1_700_000_000_001)?
110 .with_version("2.0")?;
111
112 assert_eq!(
113 params.body("csrf-token"),
114 json!({
115 "csrf": "csrf-token",
116 "platformType": 4,
117 "timestamp": 1_700_000_000_000_i64,
118 "traceId": 1_700_000_000_001_i64,
119 "version": "2.0",
120 })
121 );
122 Ok(())
123 }
124
125 #[test]
126 fn wallet_info_params_rejects_zero_platform_type() {
127 let err = WalletInfoParams::at_timestamp(1)
128 .with_platform_type(0)
129 .unwrap_err();
130
131 assert!(matches!(
132 err,
133 BpiError::InvalidParameter {
134 field: "platformType",
135 ..
136 }
137 ));
138 }
139
140 #[test]
141 fn wallet_info_params_rejects_blank_version() {
142 let err = WalletInfoParams::at_timestamp(1)
143 .with_version(" ")
144 .unwrap_err();
145
146 assert!(matches!(
147 err,
148 BpiError::InvalidParameter {
149 field: "version",
150 ..
151 }
152 ));
153 }
154
155 #[test]
156 fn wallet_info_params_trims_version() -> BpiResult<()> {
157 let params = WalletInfoParams::at_timestamp(1).with_version(" 2.0 ")?;
158
159 assert_eq!(params.body("csrf-token")["version"], "2.0");
160 Ok(())
161 }
162}