forked from proxmox-mirrors/proxmox
updaters: docs and exports
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
cc065c175d
commit
034dd3fe5e
@ -229,6 +229,87 @@ fn router_do(item: TokenStream) -> Result<TokenStream, Error> {
|
|||||||
declarations. If it contains a `schema` key, this is expected to be the path to an existing
|
declarations. If it contains a `schema` key, this is expected to be the path to an existing
|
||||||
schema. (Hence `type: Foo` is the same as `schema: Foo::API_SCHEMA`.)
|
schema. (Hence `type: Foo` is the same as `schema: Foo::API_SCHEMA`.)
|
||||||
|
|
||||||
|
# Deriving an `Updater`:
|
||||||
|
|
||||||
|
An "Updater" struct can be generated automatically for a type. This affects the `Updatable`
|
||||||
|
trait implementation generated, as it will set the associated
|
||||||
|
`type Updater = TheDerivedUpdater`.
|
||||||
|
|
||||||
|
In order to do this, simply add `#[derive(Updater)]` to the `#[api]`-macro using api type.
|
||||||
|
This is only supported for `struct`s with named fields and will generate a new `struct` whose
|
||||||
|
name is suffixed with `Updater` containing the `Updater` types of each field as a member.
|
||||||
|
|
||||||
|
Additionally the `#[updater(fixed)]` option is available to make it illegal for an updater to
|
||||||
|
modify a field (generating an error if it is set), while still allowing it to be used to create
|
||||||
|
a new object via the `build_from()` method.
|
||||||
|
|
||||||
|
```ignore
|
||||||
|
#[api]
|
||||||
|
/// An example of a simple struct type.
|
||||||
|
#[derive(Updater)]
|
||||||
|
pub struct MyType {
|
||||||
|
/// A string.
|
||||||
|
one: String,
|
||||||
|
|
||||||
|
/// An optional string.
|
||||||
|
/// Note that using `Option::is_empty` for the serde attribute only works for types which
|
||||||
|
/// use an `Option` as their `Updater`. For a `String` this works. Otherwise we'd have to
|
||||||
|
/// use `Updater::is_empty` instead.
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
opt: Option<String>,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The above will automatically generate the following:
|
||||||
|
```ignore
|
||||||
|
#[api]
|
||||||
|
/// An example of a simple struct type.
|
||||||
|
pub struct MyTypeUpdater {
|
||||||
|
one: Option<String>, // really <String as Updatable>::Updater
|
||||||
|
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
opt: Option<String>, // really <Option<String> as Updatable>::Updater
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Updater for MyTypeUpdater {
|
||||||
|
fn is_empty(&self) -> bool {
|
||||||
|
self.one.is_empty() && self.opt.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Updatable for MyType {
|
||||||
|
type Updater = MyTypeUpdater;
|
||||||
|
|
||||||
|
fn update_from<T>(&mut self, from: MyTypeUpdater, delete: &[T]) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
T: AsRef<str>,
|
||||||
|
{
|
||||||
|
for delete in delete {
|
||||||
|
match delete.as_ref() {
|
||||||
|
"opt" => { self.opt = None; }
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.one.update_from(from.one)?;
|
||||||
|
self.opt.update_from(from.opt)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_build_from(from: MyTypeUpdater) -> Result<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
// This amounts to `from.one.ok_or_else("cannot build from None value")?`
|
||||||
|
one: Updatable::try_build_from(from.one)
|
||||||
|
.map_err(|err| format_err!("failed to build value for field 'one': {}", err))?,
|
||||||
|
// This amounts to `from.opt`
|
||||||
|
opt: Updatable::try_build_from(from.opt)
|
||||||
|
.map_err(|err| format_err!("failed to build value for field 'opt': {}", err))?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
*/
|
*/
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
pub fn api(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
pub fn api(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
||||||
@ -238,12 +319,13 @@ pub fn api(attr: TokenStream_1, item: TokenStream_1) -> TokenStream_1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This is a dummy derive macro actually handled by `#[api]`!
|
/// This is a dummy derive macro actually handled by `#[api]`!
|
||||||
|
#[doc(hidden)]
|
||||||
#[proc_macro_derive(Updater, attributes(updater, updatable, serde))]
|
#[proc_macro_derive(Updater, attributes(updater, updatable, serde))]
|
||||||
pub fn derive_updater(_item: TokenStream_1) -> TokenStream_1 {
|
pub fn derive_updater(_item: TokenStream_1) -> TokenStream_1 {
|
||||||
TokenStream_1::new()
|
TokenStream_1::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create the default `Updatable` implementation from an `Option`.
|
/// Create the default `Updatable` implementation from an `Option<Self>`.
|
||||||
#[proc_macro_derive(Updatable, attributes(updatable, serde))]
|
#[proc_macro_derive(Updatable, attributes(updatable, serde))]
|
||||||
pub fn derive_updatable(item: TokenStream_1) -> TokenStream_1 {
|
pub fn derive_updatable(item: TokenStream_1) -> TokenStream_1 {
|
||||||
let _error_guard = init_local_error();
|
let _error_guard = init_local_error();
|
||||||
|
@ -1507,6 +1507,9 @@ fn test_verify_complex_array() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// API types are "updatable" in order to support derived "Updater" structs more easily.
|
/// API types are "updatable" in order to support derived "Updater" structs more easily.
|
||||||
|
///
|
||||||
|
/// By default, any API type is "updatable" by an `Option<Self>`. For types which do not use the
|
||||||
|
/// `#[api]` macro, this will need to be explicitly created (or derived via `#[derive(Updatable)]`.
|
||||||
pub trait Updatable: Sized {
|
pub trait Updatable: Sized {
|
||||||
type Updater: Updater;
|
type Updater: Updater;
|
||||||
/// This should always be true for the "default" updaters which are just `Option<T>` types.
|
/// This should always be true for the "default" updaters which are just `Option<T>` types.
|
||||||
@ -1519,6 +1522,13 @@ pub trait Updatable: Sized {
|
|||||||
fn try_build_from(from: Self::Updater) -> Result<Self, Error>;
|
fn try_build_from(from: Self::Updater) -> Result<Self, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "api-macro")]
|
||||||
|
pub use proxmox_api_macro::Updatable;
|
||||||
|
|
||||||
|
#[cfg(feature = "api-macro")]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub use proxmox_api_macro::Updater;
|
||||||
|
|
||||||
macro_rules! basic_updatable {
|
macro_rules! basic_updatable {
|
||||||
($($ty:ty)*) => {
|
($($ty:ty)*) => {
|
||||||
$(
|
$(
|
||||||
@ -1572,7 +1582,14 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A helper type for "Updater" structs.
|
/// A helper type for "Updater" structs. This trait is *not* implemented for an api "base" type
|
||||||
|
/// when deriving an `Updater` for it, though the generated *updater* type does implement this
|
||||||
|
/// trait!
|
||||||
|
///
|
||||||
|
/// This trait is mostly to figure out if an updater is empty (iow. it should not be applied).
|
||||||
|
/// In that, it is useful when a type which should have an updater also has optional fields which
|
||||||
|
/// should not be serialized. Instead of `#[serde(skip_serializing_if = "Option::is_none")]`, this
|
||||||
|
/// trait's `is_empty` needs to be used via `#[serde(skip_serializing_if = "Updater::is_empty")]`.
|
||||||
pub trait Updater {
|
pub trait Updater {
|
||||||
/// Check if the updater is "none" or "empty".
|
/// Check if the updater is "none" or "empty".
|
||||||
fn is_empty(&self) -> bool;
|
fn is_empty(&self) -> bool;
|
||||||
|
Loading…
Reference in New Issue
Block a user