]> git.proxmox.com Git - proxmox-backup.git/blame - src/tools/serde_filter.rs
update to first proxmox crate split
[proxmox-backup.git] / src / tools / serde_filter.rs
CommitLineData
59e94227
WB
1use std::marker::PhantomData;
2
3use serde::Deserialize;
4
5/// Helper to filter data while deserializing it.
6///
7/// An example use case is filtering out expired registration challenges at load time of our TFA
8/// config:
9///
10/// ```
11/// # use proxmox_backup::tools::serde_filter::FilteredVecVisitor;
12/// # use serde::{Deserialize, Deserializer, Serialize};
13/// # const CHALLENGE_TIMEOUT: i64 = 2 * 60;
14/// #[derive(Deserialize)]
15/// struct Challenge {
16/// /// Expiration time as unix epoch.
17/// expires: i64,
18///
19/// // ...other entries...
20/// }
21///
22/// #[derive(Default, Deserialize)]
23/// #[serde(deny_unknown_fields)]
24/// #[serde(rename_all = "kebab-case")]
25/// pub struct TfaUserData {
26/// // ...other entries...
27///
28/// #[serde(skip_serializing_if = "Vec::is_empty", default)]
29/// #[serde(deserialize_with = "filter_expired_registrations")]
30/// registrations: Vec<Challenge>,
31/// }
32///
33/// fn filter_expired_registrations<'de, D>(deserializer: D) -> Result<Vec<Challenge>, D::Error>
34/// where
35/// D: Deserializer<'de>,
36/// {
6ef1b649 37/// let expire_before = proxmox_time::epoch_i64() - CHALLENGE_TIMEOUT;
59e94227
WB
38///
39/// Ok(deserializer.deserialize_seq(
40/// FilteredVecVisitor::new(
41/// "a u2f registration challenge entry",
42/// move |c: &Challenge| c.expires < expire_before,
43/// )
44/// )?)
45/// }
46/// ```
47pub struct FilteredVecVisitor<F, T>
48where
49 F: Fn(&T) -> bool
50{
51 filter: F,
52 expecting: &'static str,
53 _ty: PhantomData<T>,
54}
55
56impl<F, T> FilteredVecVisitor<F, T>
57where
58 F: Fn(&T) -> bool,
59{
60 pub fn new(expecting: &'static str, filter: F) -> Self {
61 Self {
62 filter,
63 expecting,
64 _ty: PhantomData,
65 }
66 }
67}
68
69impl<'de, F, T> serde::de::Visitor<'de> for FilteredVecVisitor<F, T>
70where
71 F: Fn(&T) -> bool,
72 T: Deserialize<'de>,
73{
74 type Value = Vec<T>;
75
76 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
77 formatter.write_str(self.expecting)
78 }
79
80 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
81 where
82 A: serde::de::SeqAccess<'de>,
83 {
84 let mut out = match seq.size_hint() {
85 Some(hint) => Vec::with_capacity(hint),
86 None => Vec::new(),
87 };
88
89 while let Some(entry) = seq.next_element::<T>()? {
90 if (self.filter)(&entry) {
91 out.push(entry);
92 }
93 }
94
95 Ok(out)
96 }
97}