forked from Proxmox/proxmox
api-macro: allow referencing external schemas in properties
Reference a predefined BACKUP_ARCHIVE_NAME StringSchema like this: #[api( input: { properties: { archive: { optional: true, schema: BACKUP_ARCHIVE_NAME, }, } } )] fn get_archive(archive: String) -> Result<(), Error> { ... } Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
55f90b4eac
commit
d3ec63f26d
@ -133,11 +133,11 @@ impl Schema {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_object_property(&self, key: &str) -> Option<(bool, &Schema)> {
|
||||
fn find_object_property(&self, key: &str) -> Option<(bool, &PropertySchema)> {
|
||||
self.as_object().and_then(|obj| obj.find_property(key))
|
||||
}
|
||||
|
||||
fn find_object_property_mut(&mut self, key: &str) -> Option<(&mut bool, &mut Schema)> {
|
||||
fn find_object_property_mut(&mut self, key: &str) -> Option<(&mut bool, &mut PropertySchema)> {
|
||||
self.as_object_mut()
|
||||
.and_then(|obj| obj.find_property_mut(key))
|
||||
}
|
||||
@ -291,10 +291,28 @@ impl SchemaItem {
|
||||
}
|
||||
}
|
||||
|
||||
/// A property in an object either has its own schema or references an external schema.
|
||||
enum PropertySchema {
|
||||
Schema(Schema),
|
||||
Ref(ExprPath),
|
||||
}
|
||||
|
||||
impl PropertySchema {
|
||||
fn to_schema(&self, ts: &mut TokenStream) -> Result<(), Error> {
|
||||
match self {
|
||||
PropertySchema::Schema(s) => s.to_schema(ts),
|
||||
PropertySchema::Ref(path) => {
|
||||
ts.extend(quote! { & #path });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
/// Contains a sorted list of properties:
|
||||
struct SchemaObject {
|
||||
properties: Vec<(String, bool, Schema)>,
|
||||
properties: Vec<(String, bool, PropertySchema)>,
|
||||
}
|
||||
|
||||
impl SchemaObject {
|
||||
@ -309,6 +327,7 @@ impl SchemaObject {
|
||||
|mut properties, (key, value)| -> Result<_, syn::Error> {
|
||||
let mut schema: JSONObject =
|
||||
value.into_object("schema definition for field")?;
|
||||
|
||||
let optional: bool = schema
|
||||
.remove("optional")
|
||||
.map(|opt| -> Result<bool, syn::Error> {
|
||||
@ -317,7 +336,19 @@ impl SchemaObject {
|
||||
})
|
||||
.transpose()?
|
||||
.unwrap_or(false);
|
||||
properties.push((key.to_string(), optional, schema.try_into()?));
|
||||
|
||||
let external_schema = schema
|
||||
.remove("schema")
|
||||
.map(ExprPath::try_from)
|
||||
.transpose()?;
|
||||
|
||||
let schema = match external_schema {
|
||||
Some(path) => PropertySchema::Ref(path),
|
||||
None => PropertySchema::Schema(schema.try_into()?),
|
||||
};
|
||||
|
||||
properties.push((key.to_string(), optional, schema));
|
||||
|
||||
Ok(properties)
|
||||
},
|
||||
)
|
||||
@ -340,7 +371,7 @@ impl SchemaObject {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_property(&self, key: &str) -> Option<(bool, &Schema)> {
|
||||
fn find_property(&self, key: &str) -> Option<(bool, &PropertySchema)> {
|
||||
match self
|
||||
.properties
|
||||
.binary_search_by(|prope| prope.0.as_str().cmp(key))
|
||||
@ -350,7 +381,7 @@ impl SchemaObject {
|
||||
}
|
||||
}
|
||||
|
||||
fn find_property_mut(&mut self, key: &str) -> Option<(&mut bool, &mut Schema)> {
|
||||
fn find_property_mut(&mut self, key: &str) -> Option<(&mut bool, &mut PropertySchema)> {
|
||||
match self
|
||||
.properties
|
||||
.binary_search_by(|prope| prope.0.as_str().cmp(key))
|
||||
|
@ -8,7 +8,7 @@ use quote::{quote, quote_spanned};
|
||||
use syn::spanned::Spanned;
|
||||
use syn::Ident;
|
||||
|
||||
use super::{Schema, SchemaItem};
|
||||
use super::{PropertySchema, Schema, SchemaItem};
|
||||
use crate::util::{BareAssignment, JSONObject, SimpleIdent};
|
||||
|
||||
/// Parse `input`, `returns` and `protected` attributes out of an function annotated
|
||||
@ -161,7 +161,7 @@ enum ParameterType<'a> {
|
||||
Value,
|
||||
ApiMethod,
|
||||
RpcEnv,
|
||||
Other(&'a syn::Type, bool, &'a Schema),
|
||||
Other(&'a syn::Type, bool, &'a PropertySchema),
|
||||
}
|
||||
|
||||
fn check_input_type(input: &syn::FnArg) -> Result<(&syn::PatType, &syn::PatIdent), Error> {
|
||||
@ -205,11 +205,14 @@ fn handle_function_signature(
|
||||
let schema: &mut Schema = if let Some((_optional, schema)) =
|
||||
input_schema.find_object_property_mut(&pat.ident.to_string())
|
||||
{
|
||||
// ... if it has no `type` property (item = SchemaItem::Inferred), get a mutable
|
||||
// reference to the schema, so that we can...
|
||||
match &mut schema.item {
|
||||
SchemaItem::Inferred(_span) => schema,
|
||||
// other types are fine:
|
||||
match schema {
|
||||
PropertySchema::Schema(schema) => match &mut schema.item {
|
||||
// ... if it has no `type` property (item = SchemaItem::Inferred), get a mutable
|
||||
// reference to the schema, so that we can...
|
||||
SchemaItem::Inferred(_span) => schema,
|
||||
// other types are fine:
|
||||
_ => continue,
|
||||
},
|
||||
_ => continue,
|
||||
}
|
||||
} else {
|
||||
@ -268,8 +271,11 @@ fn handle_function_signature(
|
||||
let param_type = if let Some((optional, schema)) =
|
||||
input_schema.find_object_property(&pat.ident.to_string())
|
||||
{
|
||||
match &schema.item {
|
||||
SchemaItem::Inferred(span) => bail!(*span, "failed to infer type"),
|
||||
match schema {
|
||||
PropertySchema::Schema(schema) => match &schema.item {
|
||||
SchemaItem::Inferred(span) => bail!(*span, "failed to infer type"),
|
||||
_ => (),
|
||||
},
|
||||
_ => (),
|
||||
}
|
||||
// Found an explicit parameter: extract it:
|
||||
|
26
proxmox-api-macro/tests/ext-schema.rs
Normal file
26
proxmox-api-macro/tests/ext-schema.rs
Normal file
@ -0,0 +1,26 @@
|
||||
//! This should test the usage of "external" schemas. If a property is declared with a path instead
|
||||
//! of an object, we expect the path to lead to a schema.
|
||||
|
||||
use proxmox::api::schema;
|
||||
use proxmox_api_macro::api;
|
||||
|
||||
use failure::Error;
|
||||
|
||||
pub const NAME_SCHEMA: schema::Schema = schema::StringSchema::new("Archive name.")
|
||||
//.format(&FILENAME_FORMAT)
|
||||
.schema();
|
||||
|
||||
#[api(
|
||||
input: {
|
||||
properties: {
|
||||
name: {
|
||||
schema: NAME_SCHEMA,
|
||||
}
|
||||
}
|
||||
}
|
||||
)]
|
||||
/// Get an archive.
|
||||
pub fn get_archive(name: String) -> Result<(), Error> {
|
||||
let _ = name;
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue
Block a user