reqwest_builder/
serialization.rs1use crate::errors::ReqwestBuilderError;
2use http::HeaderMap;
3use serde::Serialize;
4use std::collections::HashMap;
5
6pub fn serialize_to_form_params_safe<T: Serialize>(data: &T) -> HashMap<String, String> {
8 serde_json::to_value(data)
9 .ok()
10 .and_then(|v| v.as_object().cloned())
11 .map(|obj| {
12 obj.iter()
13 .filter_map(|(key, val)| {
14 let value_str = match val {
15 serde_json::Value::String(s) => s.clone(),
16 serde_json::Value::Number(n) => n.to_string(),
17 serde_json::Value::Bool(b) => b.to_string(),
18 serde_json::Value::Null => return None, _ => val.to_string(), };
21 Some((key.clone(), value_str))
22 })
23 .collect()
24 })
25 .unwrap_or_default()
26}
27
28pub fn serialize_to_form_params<T: Serialize>(
30 data: &T,
31) -> std::result::Result<HashMap<String, String>, ReqwestBuilderError> {
32 let value = serde_json::to_value(data)?;
33
34 let obj = value.as_object().ok_or_else(|| {
35 ReqwestBuilderError::SerializationError("Data must serialize to a JSON object".to_string())
36 })?;
37
38 let mut params = HashMap::new();
39 for (key, val) in obj {
40 let value_str = match val {
41 serde_json::Value::String(s) => s.clone(),
42 serde_json::Value::Number(n) => n.to_string(),
43 serde_json::Value::Bool(b) => b.to_string(),
44 serde_json::Value::Null => continue, _ => val.to_string(), };
47 params.insert(key.clone(), value_str);
48 }
49
50 Ok(params)
51}
52
53pub fn serialize_to_header_map_safe<T: Serialize>(headers: &T) -> HeaderMap {
55 let mut header_map = HeaderMap::new();
56
57 if let Ok(value) = serde_json::to_value(headers) {
58 if let Some(obj) = value.as_object() {
59 for (key, val) in obj {
60 if let Some(val_str) = val.as_str() {
61 if let (Ok(header_name), Ok(header_value)) = (
62 http::HeaderName::from_bytes(key.as_bytes()),
63 http::HeaderValue::from_str(val_str),
64 ) {
65 header_map.insert(header_name, header_value);
66 }
67 }
69 }
70 }
71 }
72
73 header_map
74}
75
76pub fn serialize_to_header_map<T: Serialize>(
78 headers: &T,
79) -> std::result::Result<HeaderMap, ReqwestBuilderError> {
80 let mut header_map = HeaderMap::new();
81 let value = serde_json::to_value(headers)?;
82
83 let obj = value.as_object().ok_or_else(|| {
84 ReqwestBuilderError::SerializationError(
85 "Headers must serialize to a JSON object".to_string(),
86 )
87 })?;
88
89 for (key, val) in obj {
90 if let Some(val_str) = val.as_str() {
91 let header_name = http::HeaderName::from_bytes(key.as_bytes()).map_err(|e| {
92 ReqwestBuilderError::HeaderError {
93 key: key.clone(),
94 value: val_str.to_string(),
95 source: format!("Invalid header name: {}", e),
96 }
97 })?;
98
99 let header_value = http::HeaderValue::from_str(val_str).map_err(|e| {
100 ReqwestBuilderError::HeaderError {
101 key: key.clone(),
102 value: val_str.to_string(),
103 source: format!("Invalid header value: {}", e),
104 }
105 })?;
106
107 header_map.insert(header_name, header_value);
108 } else {
109 return Err(ReqwestBuilderError::HeaderError {
110 key: key.clone(),
111 value: val.to_string(),
112 source: "Header value must be a string".to_string(),
113 });
114 }
115 }
116
117 Ok(header_map)
118}
119
120pub fn construct_url_safe(base_url: &url::Url, endpoint: &str) -> String {
122 let base_str = base_url.as_str().trim_end_matches('/');
123 let endpoint_str = endpoint.trim_start_matches('/');
124
125 if endpoint_str.is_empty() {
126 return base_str.to_string();
127 }
128
129 format!("{base_str}/{endpoint_str}")
130}