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() => Ok(quote_spanned! { name.span() =>
#enum_ty #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 #schema
.format(&::proxmox::api::schema::ApiStringFormat::Enum(&[#variants])) .format(&::proxmox::api::schema::ApiStringFormat::Enum(&[#variants]))
.schema(); .schema();

View File

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

View File

@ -84,8 +84,10 @@ fn finish_schema(
Ok(quote_spanned! { name.span() => Ok(quote_spanned! { name.span() =>
#stru #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() => Ok(quote_spanned!(name.span() =>
#stru #stru
impl #name { impl #name {
#inner_schema #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( ::proxmox::api::schema::AllOfSchema::new(
#description, #description,
&[ &[
@ -528,7 +535,7 @@ fn handle_updater_field(
if field_schema.flatten_in_struct { if field_schema.flatten_in_struct {
let updater_ty = &field.ty; 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() { if !is_empty_impl.is_empty() {

View File

@ -1,5 +1,15 @@
use proxmox::api::api; 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] #[api]
/// An example of a simple struct type. /// An example of a simple struct type.
@ -38,7 +48,7 @@ pub struct Complex {
}, },
)] )]
/// One of the baaaad cases. /// One of the baaaad cases.
#[derive(Default, Updater)] #[derive(Updater)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
pub struct SuperComplex { pub struct SuperComplex {
/// An extra field not part of the flattened struct. /// 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. /// A field which should not appear in the updater.
#[updater(skip)] #[updater(skip)]
not_in_updater: String, 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; 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 /// 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 /// when deriving an `Updater` for it, though the generated *updater* type does implement this
/// trait! /// trait!