mirror of
https://git.proxmox.com/git/proxmox
synced 2025-07-23 13:38:29 +00:00
schema: introduce Updatable and Updater traits
The `Updatable` trait is our mechanism for "updating" values. The `Updater` trait is a serde-helper to allow using `skip_serializing_if` on api type structs which `#[derive(Updater)]` All basic types implement `Updatable<Updater = Option<T>>`, and all `Option<T>` where `T: Updatable` are implicitly also updatable from `Option<T>`. With the update to the `#[api]` macro, all `#[api]` types will implicitly `#[derive(Updatable)]` via the default implementation (which simply assignes from an `Option<Self>`), unless they explicitly also `#[derive(Updater)]` in which case an implementation is derived which goes through each "field" found in the updater. To prevent fields from being updatable via an Updater `#[updater(fixed)]` can be used. Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
237f206235
commit
ab06665649
@ -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<T>` types.
|
||||
/// Types which are not wrapped in `Option` must set this to `false`.
|
||||
const UPDATER_IS_OPTION: bool;
|
||||
|
||||
fn update_from<T>(&mut self, from: Self::Updater, delete: &[T]) -> Result<(), Error>
|
||||
where
|
||||
T: AsRef<str>;
|
||||
fn try_build_from(from: Self::Updater) -> Result<Self, Error>;
|
||||
}
|
||||
|
||||
macro_rules! basic_updatable {
|
||||
($($ty:ty)*) => {
|
||||
$(
|
||||
impl Updatable for $ty {
|
||||
type Updater = Option<$ty>;
|
||||
const UPDATER_IS_OPTION: bool = true;
|
||||
|
||||
fn update_from<T: AsRef<str>>(
|
||||
&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<Self, Error> {
|
||||
from.ok_or_else(|| format_err!("cannot build from None value"))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
basic_updatable! { bool u8 u16 u32 u64 i8 i16 i32 i64 String }
|
||||
|
||||
impl<T> Updatable for Option<T>
|
||||
where
|
||||
T: Updatable,
|
||||
{
|
||||
type Updater = T::Updater;
|
||||
const UPDATER_IS_OPTION: bool = true;
|
||||
|
||||
fn update_from<S: AsRef<str>>(&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<Self, Error> {
|
||||
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<T> Updater for Vec<T> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Updater for Option<T> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.is_none()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user