put API_SCHEMA variable into ApiType trait

This way we can assign `API_SCHEMA` constants to `Option`
types.

Here's why:

The api-macro generated code usese `T::API_SCHEMA` when
building ObjectSchemas.

For Updaters we replace `T` with
  `<T as Updatable>::Updater`

This means for "simple" wrappers like our `Authid` or
`Userid`, the ObjectSchema will try to refer to
  `<Authid as Updatable>::Updater::API_SCHEMA`
which resolves to:
  `Option<Authid>::API_SCHEMA`
which does not exist, for which we cannot add a normal
`impl` block to add the schema variable, since `Option` is
not "ours".

But we now have a blanket implementation of `ApiType` for
`Option<T> where T: ApiType` which just points to the
original `T::API_SCHEMA`.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2021-08-12 10:15:12 +02:00
parent 783cbcb499
commit cb9d57b453
5 changed files with 40 additions and 10 deletions

View File

@ -76,8 +76,10 @@ pub fn handle_enum(
Ok(quote_spanned! { name.span() =>
#enum_ty
impl #name {
pub const API_SCHEMA: ::proxmox::api::schema::Schema =
#[automatically_derived]
impl ::proxmox::api::schema::ApiType for #name {
const API_SCHEMA: ::proxmox::api::schema::Schema =
#schema
.format(&::proxmox::api::schema::ApiStringFormat::Enum(&[#variants]))
.schema();

View File

@ -148,7 +148,7 @@ impl Schema {
fn to_schema_reference(&self) -> Option<TokenStream> {
match &self.item {
SchemaItem::ExternType(path) => {
Some(quote_spanned! { path.span() => &#path::API_SCHEMA })
Some(quote_spanned! { path.span() => &<#path as ::proxmox::api::schema::ApiType>::API_SCHEMA })
}
SchemaItem::ExternSchema(path) => Some(quote_spanned! { path.span() => &#path }),
_ => None,
@ -375,7 +375,7 @@ impl SchemaItem {
error!(description => "description not allowed on external type");
}
ts.extend(quote_spanned! { path.span() => #path::API_SCHEMA });
ts.extend(quote_spanned! { path.span() => <#path as ::proxmox::api::schema::ApiType>::API_SCHEMA });
return Ok(true);
}
SchemaItem::ExternSchema(path) => {

View File

@ -84,8 +84,10 @@ fn finish_schema(
Ok(quote_spanned! { name.span() =>
#stru
impl #name {
pub const API_SCHEMA: ::proxmox::api::schema::Schema = #schema;
#[automatically_derived]
impl ::proxmox::api::schema::ApiType for #name {
const API_SCHEMA: ::proxmox::api::schema::Schema = #schema;
}
})
}
@ -347,9 +349,14 @@ fn finish_all_of_struct(
Ok(quote_spanned!(name.span() =>
#stru
impl #name {
#inner_schema
pub const API_SCHEMA: ::proxmox::api::schema::Schema =
}
#[automatically_derived]
impl ::proxmox::api::schema::ApiType for #name {
const API_SCHEMA: ::proxmox::api::schema::Schema =
::proxmox::api::schema::AllOfSchema::new(
#description,
&[
@ -528,7 +535,7 @@ fn handle_updater_field(
if field_schema.flatten_in_struct {
let updater_ty = &field.ty;
all_of_schemas.extend(quote::quote! {&#updater_ty::API_SCHEMA,});
all_of_schemas.extend(quote::quote! {&<#updater_ty as ::proxmox::api::schema::ApiType>::API_SCHEMA,});
}
if !is_empty_impl.is_empty() {

View File

@ -1,5 +1,15 @@
use proxmox::api::api;
use proxmox::api::schema::Updater;
use proxmox::api::schema::{Schema, StringSchema, Updatable, Updater};
#[derive(Updatable)]
pub struct Custom(String);
impl proxmox::api::schema::ApiType for Custom {
const API_SCHEMA: Schema = StringSchema::new("Custom String")
.min_length(3)
.max_length(64)
.schema();
}
#[api]
/// An example of a simple struct type.
@ -38,7 +48,7 @@ pub struct Complex {
},
)]
/// One of the baaaad cases.
#[derive(Default, Updater)]
#[derive(Updater)]
#[serde(rename_all = "kebab-case")]
pub struct SuperComplex {
/// An extra field not part of the flattened struct.
@ -50,4 +60,7 @@ pub struct SuperComplex {
/// A field which should not appear in the updater.
#[updater(skip)]
not_in_updater: String,
/// A custom type with an Updatable implementation.
custom: Custom,
}

View File

@ -1560,6 +1560,14 @@ where
const UPDATER_IS_OPTION: bool = true;
}
pub trait ApiType {
const API_SCHEMA: Schema;
}
impl<T: ApiType> ApiType for Option<T> {
const API_SCHEMA: Schema = T::API_SCHEMA;
}
/// 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!