Skip to main content

tdm_server_rust/utils/
query_deserialize.rs

1//! Query 参数反序列化辅助 (Query Deserialization)
2//!
3//! 兼容前端传空字符串、NaN、undefined 等非法值,
4//! 对齐 Java/Spring 的参数绑定行为。
5
6use serde::{de, Deserialize, Deserializer};
7use std::fmt::Display;
8use std::str::FromStr;
9
10/// 前端占位非法值(NaN / undefined / 空串)视为缺失
11fn is_invalid_query_token(s: &str) -> bool {
12    let t = s.trim();
13    t.is_empty() || t.eq_ignore_ascii_case("nan") || t.eq_ignore_ascii_case("undefined")
14}
15
16fn empty_as_none<'de, T, D>(deserializer: D, _field: &str) -> Result<Option<T>, D::Error>
17where
18    D: Deserializer<'de>,
19    T: FromStr,
20    T::Err: Display,
21{
22    let raw = Option::<String>::deserialize(deserializer)?;
23    match raw {
24        None => Ok(None),
25        Some(s) if is_invalid_query_token(&s) => Ok(None),
26        Some(s) => s.parse().map(Some).map_err(de::Error::custom),
27    }
28}
29
30fn empty_as_i32_default<'de, D>(
31    deserializer: D,
32    default: i32,
33    _field: &str,
34) -> Result<i32, D::Error>
35where
36    D: Deserializer<'de>,
37{
38    let raw = Option::<String>::deserialize(deserializer)?;
39    match raw {
40        None => Ok(default),
41        Some(s) if s.trim().is_empty() => Ok(default),
42        Some(s) => s.parse().map_err(de::Error::custom),
43    }
44}
45
46fn required_i32<'de, D>(deserializer: D, field: &str) -> Result<i32, D::Error>
47where
48    D: Deserializer<'de>,
49{
50    let raw = Option::<String>::deserialize(deserializer)?;
51    match raw {
52        None => Err(de::Error::custom(format!("missing query parameter `{field}`"))),
53        Some(s) if s.trim().is_empty() => {
54            Err(de::Error::custom(format!("empty query parameter `{field}`")))
55        }
56        Some(s) => s.parse().map_err(de::Error::custom),
57    }
58}
59
60fn required_i16<'de, D>(deserializer: D, field: &str) -> Result<i16, D::Error>
61where
62    D: Deserializer<'de>,
63{
64    let raw = Option::<String>::deserialize(deserializer)?;
65    match raw {
66        None => Err(de::Error::custom(format!("missing query parameter `{field}`"))),
67        Some(s) if s.trim().is_empty() => {
68            Err(de::Error::custom(format!("empty query parameter `{field}`")))
69        }
70        Some(s) => s.parse().map_err(de::Error::custom),
71    }
72}
73
74/// 可选字符串,空/空白视为 None(对齐 Java `!= ''`)
75pub fn de_opt_string<'de, D>(d: D) -> Result<Option<String>, D::Error>
76where
77    D: Deserializer<'de>,
78{
79    let raw = Option::<String>::deserialize(d)?;
80    Ok(match raw {
81        None => None,
82        Some(s) if s.trim().is_empty() => None,
83        Some(s) => Some(s),
84    })
85}
86
87/// 可选 i16,空字符串视为 None
88pub fn de_opt_i16<'de, D>(d: D) -> Result<Option<i16>, D::Error>
89where
90    D: Deserializer<'de>,
91{
92    empty_as_none(d, "i16")
93}
94
95/// 可选 i32,空字符串视为 None
96pub fn de_opt_i32<'de, D>(d: D) -> Result<Option<i32>, D::Error>
97where
98    D: Deserializer<'de>,
99{
100    empty_as_none(d, "i32")
101}
102
103/// 页码,缺省或空字符串为 1
104pub fn de_page<'de, D>(d: D) -> Result<i32, D::Error>
105where
106    D: Deserializer<'de>,
107{
108    empty_as_i32_default(d, 1, "page")
109}
110
111/// 每页条数,缺省或空字符串为 20
112pub fn de_page_size<'de, D>(d: D) -> Result<i32, D::Error>
113where
114    D: Deserializer<'de>,
115{
116    empty_as_i32_default(d, 20, "pageSize")
117}
118
119/// 常驻组员分页大小,缺省或空字符串为 5
120pub fn de_station_page_size<'de, D>(d: D) -> Result<i32, D::Error>
121where
122    D: Deserializer<'de>,
123{
124    empty_as_i32_default(d, 5, "pageSize")
125}
126
127/// 必填 i32
128pub fn de_i32<'de, D>(d: D) -> Result<i32, D::Error>
129where
130    D: Deserializer<'de>,
131{
132    required_i32(d, "i32")
133}
134
135/// 必填 i16
136pub fn de_i16<'de, D>(d: D) -> Result<i16, D::Error>
137where
138    D: Deserializer<'de>,
139{
140    required_i16(d, "i16")
141}
142
143/// JSON 可选 i32(兼容数字或字符串)
144pub fn de_opt_i32_loose<'de, D>(d: D) -> Result<Option<i32>, D::Error>
145where
146    D: Deserializer<'de>,
147{
148    #[derive(Deserialize)]
149    #[serde(untagged)]
150    enum NumOrStr {
151        Num(i32),
152        Str(String),
153    }
154    match Option::<NumOrStr>::deserialize(d)? {
155        None => Ok(None),
156        Some(NumOrStr::Num(v)) => Ok(Some(v)),
157        Some(NumOrStr::Str(s)) if s.trim().is_empty() => Ok(None),
158        Some(NumOrStr::Str(s)) => s.parse().map(Some).map_err(de::Error::custom),
159    }
160}
161
162/// JSON 可选 i16(兼容数字或字符串)
163pub fn de_opt_i16_loose<'de, D>(d: D) -> Result<Option<i16>, D::Error>
164where
165    D: Deserializer<'de>,
166{
167    #[derive(Deserialize)]
168    #[serde(untagged)]
169    enum NumOrStr {
170        Num(i16),
171        Str(String),
172    }
173    match Option::<NumOrStr>::deserialize(d)? {
174        None => Ok(None),
175        Some(NumOrStr::Num(v)) => Ok(Some(v)),
176        Some(NumOrStr::Str(s)) if s.trim().is_empty() => Ok(None),
177        Some(NumOrStr::Str(s)) => s.parse().map(Some).map_err(de::Error::custom),
178    }
179}
180
181/// JSON 必填 i32(兼容数字或字符串,对齐前端 form 字符串字段)
182pub fn de_i32_loose<'de, D>(d: D) -> Result<i32, D::Error>
183where
184    D: Deserializer<'de>,
185{
186    #[derive(Deserialize)]
187    #[serde(untagged)]
188    enum NumOrStr {
189        Num(i32),
190        Str(String),
191    }
192    match NumOrStr::deserialize(d)? {
193        NumOrStr::Num(v) => Ok(v),
194        NumOrStr::Str(s) if s.trim().is_empty() => {
195            Err(de::Error::custom("required i32 field is empty"))
196        }
197        NumOrStr::Str(s) => s.parse().map_err(de::Error::custom),
198    }
199}