add tools::serde_filter submodule
can be used to perform filtering at parse time Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
		@ -33,6 +33,7 @@ pub mod loopdev;
 | 
				
			|||||||
pub mod lru_cache;
 | 
					pub mod lru_cache;
 | 
				
			||||||
pub mod nom;
 | 
					pub mod nom;
 | 
				
			||||||
pub mod runtime;
 | 
					pub mod runtime;
 | 
				
			||||||
 | 
					pub mod serde_filter;
 | 
				
			||||||
pub mod socket;
 | 
					pub mod socket;
 | 
				
			||||||
pub mod statistics;
 | 
					pub mod statistics;
 | 
				
			||||||
pub mod subscription;
 | 
					pub mod subscription;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										97
									
								
								src/tools/serde_filter.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										97
									
								
								src/tools/serde_filter.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					use std::marker::PhantomData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use serde::Deserialize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Helper to filter data while deserializing it.
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// An example use case is filtering out expired registration challenges at load time of our TFA
 | 
				
			||||||
 | 
					/// config:
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					/// # use proxmox_backup::tools::serde_filter::FilteredVecVisitor;
 | 
				
			||||||
 | 
					/// # use serde::{Deserialize, Deserializer, Serialize};
 | 
				
			||||||
 | 
					/// # const CHALLENGE_TIMEOUT: i64 = 2 * 60;
 | 
				
			||||||
 | 
					/// #[derive(Deserialize)]
 | 
				
			||||||
 | 
					/// struct Challenge {
 | 
				
			||||||
 | 
					///     /// Expiration time as unix epoch.
 | 
				
			||||||
 | 
					///     expires: i64,
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///     // ...other entries...
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// #[derive(Default, Deserialize)]
 | 
				
			||||||
 | 
					/// #[serde(deny_unknown_fields)]
 | 
				
			||||||
 | 
					/// #[serde(rename_all = "kebab-case")]
 | 
				
			||||||
 | 
					/// pub struct TfaUserData {
 | 
				
			||||||
 | 
					///     // ...other entries...
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///     #[serde(skip_serializing_if = "Vec::is_empty", default)]
 | 
				
			||||||
 | 
					///     #[serde(deserialize_with = "filter_expired_registrations")]
 | 
				
			||||||
 | 
					///     registrations: Vec<Challenge>,
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					/// fn filter_expired_registrations<'de, D>(deserializer: D) -> Result<Vec<Challenge>, D::Error>
 | 
				
			||||||
 | 
					/// where
 | 
				
			||||||
 | 
					///     D: Deserializer<'de>,
 | 
				
			||||||
 | 
					/// {
 | 
				
			||||||
 | 
					///     let expire_before = proxmox::tools::time::epoch_i64() - CHALLENGE_TIMEOUT;
 | 
				
			||||||
 | 
					///
 | 
				
			||||||
 | 
					///     Ok(deserializer.deserialize_seq(
 | 
				
			||||||
 | 
					///         FilteredVecVisitor::new(
 | 
				
			||||||
 | 
					///             "a u2f registration challenge entry",
 | 
				
			||||||
 | 
					///             move |c: &Challenge| c.expires < expire_before,
 | 
				
			||||||
 | 
					///         )
 | 
				
			||||||
 | 
					///     )?)
 | 
				
			||||||
 | 
					/// }
 | 
				
			||||||
 | 
					/// ```
 | 
				
			||||||
 | 
					pub struct FilteredVecVisitor<F, T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    F: Fn(&T) -> bool
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    filter: F,
 | 
				
			||||||
 | 
					    expecting: &'static str,
 | 
				
			||||||
 | 
					    _ty: PhantomData<T>,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<F, T> FilteredVecVisitor<F, T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    F: Fn(&T) -> bool,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    pub fn new(expecting: &'static str, filter: F) -> Self {
 | 
				
			||||||
 | 
					        Self {
 | 
				
			||||||
 | 
					            filter,
 | 
				
			||||||
 | 
					            expecting,
 | 
				
			||||||
 | 
					            _ty: PhantomData,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					impl<'de, F, T> serde::de::Visitor<'de> for FilteredVecVisitor<F, T>
 | 
				
			||||||
 | 
					where
 | 
				
			||||||
 | 
					    F: Fn(&T) -> bool,
 | 
				
			||||||
 | 
					    T: Deserialize<'de>,
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    type Value = Vec<T>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
 | 
				
			||||||
 | 
					        formatter.write_str(self.expecting)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
 | 
				
			||||||
 | 
					    where
 | 
				
			||||||
 | 
					        A: serde::de::SeqAccess<'de>,
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        let mut out = match seq.size_hint() {
 | 
				
			||||||
 | 
					            Some(hint) => Vec::with_capacity(hint),
 | 
				
			||||||
 | 
					            None => Vec::new(),
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while let Some(entry) = seq.next_element::<T>()? {
 | 
				
			||||||
 | 
					            if (self.filter)(&entry) {
 | 
				
			||||||
 | 
					                out.push(entry);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ok(out)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user