diff --git a/proxmox/src/api/schema.rs b/proxmox/src/api/schema.rs index dea83361..257265ad 100644 --- a/proxmox/src/api/schema.rs +++ b/proxmox/src/api/schema.rs @@ -1505,3 +1505,87 @@ fn test_verify_complex_array() { assert!(res.is_err()); } } + +/// API types are "updatable" in order to support derived "Updater" structs more easily. +pub trait Updatable: Sized { + type Updater: Updater; + /// This should always be true for the "default" updaters which are just `Option` types. + /// Types which are not wrapped in `Option` must set this to `false`. + const UPDATER_IS_OPTION: bool; + + fn update_from(&mut self, from: Self::Updater, delete: &[T]) -> Result<(), Error> + where + T: AsRef; + fn try_build_from(from: Self::Updater) -> Result; +} + +macro_rules! basic_updatable { + ($($ty:ty)*) => { + $( + impl Updatable for $ty { + type Updater = Option<$ty>; + const UPDATER_IS_OPTION: bool = true; + + fn update_from>( + &mut self, + from: Option<$ty>, + _delete: &[T], + ) -> Result<(), Error> { + if let Some(val) = from { + *self = val; + } + Ok(()) + } + + fn try_build_from(from: Option<$ty>) -> Result { + from.ok_or_else(|| format_err!("cannot build from None value")) + } + } + )* + }; +} +basic_updatable! { bool u8 u16 u32 u64 i8 i16 i32 i64 String } + +impl Updatable for Option +where + T: Updatable, +{ + type Updater = T::Updater; + const UPDATER_IS_OPTION: bool = true; + + fn update_from>(&mut self, from: T::Updater, delete: &[S]) -> Result<(), Error> { + match self { + Some(val) => val.update_from(from, delete), + None => { + *self = Self::try_build_from(from)?; + Ok(()) + } + } + } + + fn try_build_from(from: T::Updater) -> Result { + if from.is_empty() { + Ok(None) + } else { + T::try_build_from(from).map(Some) + } + } +} + +/// A helper type for "Updater" structs. +pub trait Updater { + /// Check if the updater is "none" or "empty". + fn is_empty(&self) -> bool; +} + +impl Updater for Vec { + fn is_empty(&self) -> bool { + self.is_empty() + } +} + +impl Updater for Option { + fn is_empty(&self) -> bool { + self.is_none() + } +}