api-macro: object schema entry tuple -> struct
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
de749b1a52
commit
75530cb68c
@ -263,16 +263,14 @@ fn handle_function_signature(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// For any named type which exists on the function signature...
|
// For any named type which exists on the function signature...
|
||||||
if let Some((_ident, optional, ref mut schema)) =
|
if let Some(entry) = input_schema.find_obj_property_by_ident_mut(&pat.ident.to_string()) {
|
||||||
input_schema.find_obj_property_by_ident_mut(&pat.ident.to_string())
|
|
||||||
{
|
|
||||||
// try to infer the type in the schema if it is not specified explicitly:
|
// try to infer the type in the schema if it is not specified explicitly:
|
||||||
let is_option = util::infer_type(schema, &*pat_type.ty)?;
|
let is_option = util::infer_type(&mut entry.schema, &*pat_type.ty)?;
|
||||||
let has_default = schema.find_schema_property("default").is_some();
|
let has_default = entry.schema.find_schema_property("default").is_some();
|
||||||
if !is_option && *optional && !has_default {
|
if !is_option && entry.optional && !has_default {
|
||||||
error!(pat_type => "optional types need a default or be an Option<T>");
|
error!(pat_type => "optional types need a default or be an Option<T>");
|
||||||
}
|
}
|
||||||
if has_default && !*optional {
|
if has_default && !entry.optional {
|
||||||
error!(pat_type => "non-optional parameter cannot have a default");
|
error!(pat_type => "non-optional parameter cannot have a default");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -313,40 +311,39 @@ fn handle_function_signature(
|
|||||||
// bail out with an error.
|
// bail out with an error.
|
||||||
let pat_ident = pat.ident.unraw();
|
let pat_ident = pat.ident.unraw();
|
||||||
let mut param_name: FieldName = pat_ident.clone().into();
|
let mut param_name: FieldName = pat_ident.clone().into();
|
||||||
let param_type = if let Some((name, optional, schema)) =
|
let param_type =
|
||||||
input_schema.find_obj_property_by_ident(&pat_ident.to_string())
|
if let Some(entry) = input_schema.find_obj_property_by_ident(&pat_ident.to_string()) {
|
||||||
{
|
if let SchemaItem::Inferred(span) = &entry.schema.item {
|
||||||
if let SchemaItem::Inferred(span) = &schema.item {
|
bail!(*span, "failed to infer type");
|
||||||
bail!(*span, "failed to infer type");
|
}
|
||||||
}
|
param_name = entry.name.clone();
|
||||||
param_name = name.clone();
|
// Found an explicit parameter: extract it:
|
||||||
// Found an explicit parameter: extract it:
|
ParameterType::Other(&pat_type.ty, entry.optional, &entry.schema)
|
||||||
ParameterType::Other(&pat_type.ty, *optional, schema)
|
} else if is_api_method_type(&pat_type.ty) {
|
||||||
} else if is_api_method_type(&pat_type.ty) {
|
if api_method_param.is_some() {
|
||||||
if api_method_param.is_some() {
|
error!(pat_type => "multiple ApiMethod parameters found");
|
||||||
error!(pat_type => "multiple ApiMethod parameters found");
|
continue;
|
||||||
|
}
|
||||||
|
api_method_param = Some(param_list.len());
|
||||||
|
ParameterType::ApiMethod
|
||||||
|
} else if is_rpc_env_type(&pat_type.ty) {
|
||||||
|
if rpc_env_param.is_some() {
|
||||||
|
error!(pat_type => "multiple RpcEnvironment parameters found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
rpc_env_param = Some(param_list.len());
|
||||||
|
ParameterType::RpcEnv
|
||||||
|
} else if is_value_type(&pat_type.ty) {
|
||||||
|
if value_param.is_some() {
|
||||||
|
error!(pat_type => "multiple additional Value parameters found");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
value_param = Some(param_list.len());
|
||||||
|
ParameterType::Value
|
||||||
|
} else {
|
||||||
|
error!(&pat_ident => "unexpected parameter {:?}", pat_ident.to_string());
|
||||||
continue;
|
continue;
|
||||||
}
|
};
|
||||||
api_method_param = Some(param_list.len());
|
|
||||||
ParameterType::ApiMethod
|
|
||||||
} else if is_rpc_env_type(&pat_type.ty) {
|
|
||||||
if rpc_env_param.is_some() {
|
|
||||||
error!(pat_type => "multiple RpcEnvironment parameters found");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
rpc_env_param = Some(param_list.len());
|
|
||||||
ParameterType::RpcEnv
|
|
||||||
} else if is_value_type(&pat_type.ty) {
|
|
||||||
if value_param.is_some() {
|
|
||||||
error!(pat_type => "multiple additional Value parameters found");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
value_param = Some(param_list.len());
|
|
||||||
ParameterType::Value
|
|
||||||
} else {
|
|
||||||
error!(&pat_ident => "unexpected parameter {:?}", pat_ident.to_string());
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
param_list.push((param_name, param_type));
|
param_list.push((param_name, param_type));
|
||||||
}
|
}
|
||||||
@ -594,7 +591,7 @@ impl<'a> DefaultParameters<'a> {
|
|||||||
fn get_default(&self, param_tokens: TokenStream) -> Result<syn::Expr, syn::Error> {
|
fn get_default(&self, param_tokens: TokenStream) -> Result<syn::Expr, syn::Error> {
|
||||||
let param_name: syn::LitStr = syn::parse2(param_tokens)?;
|
let param_name: syn::LitStr = syn::parse2(param_tokens)?;
|
||||||
match self.0.find_obj_property_by_ident(¶m_name.value()) {
|
match self.0.find_obj_property_by_ident(¶m_name.value()) {
|
||||||
Some((_ident, _optional, schema)) => match schema.find_schema_property("default") {
|
Some(entry) => match entry.schema.find_schema_property("default") {
|
||||||
Some(def) => Ok(def.clone()),
|
Some(def) => Ok(def.clone()),
|
||||||
None => bail!(param_name => "no default found in schema"),
|
None => bail!(param_name => "no default found in schema"),
|
||||||
},
|
},
|
||||||
|
@ -176,15 +176,12 @@ impl Schema {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_obj_property_by_ident(&self, key: &str) -> Option<&(FieldName, bool, Schema)> {
|
fn find_obj_property_by_ident(&self, key: &str) -> Option<&ObjectEntry> {
|
||||||
self.as_object()
|
self.as_object()
|
||||||
.and_then(|obj| obj.find_property_by_ident(key))
|
.and_then(|obj| obj.find_property_by_ident(key))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_obj_property_by_ident_mut(
|
fn find_obj_property_by_ident_mut(&mut self, key: &str) -> Option<&mut ObjectEntry> {
|
||||||
&mut self,
|
|
||||||
key: &str,
|
|
||||||
) -> Option<&mut (FieldName, bool, Schema)> {
|
|
||||||
self.as_object_mut()
|
self.as_object_mut()
|
||||||
.and_then(|obj| obj.find_property_by_ident_mut(key))
|
.and_then(|obj| obj.find_property_by_ident_mut(key))
|
||||||
}
|
}
|
||||||
@ -384,10 +381,26 @@ impl SchemaItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct ObjectEntry {
|
||||||
|
pub name: FieldName,
|
||||||
|
pub optional: bool,
|
||||||
|
pub schema: Schema,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ObjectEntry {
|
||||||
|
pub fn new(name: FieldName, optional: bool, schema: Schema) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
optional,
|
||||||
|
schema,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
/// Contains a sorted list of properties:
|
/// Contains a sorted list of properties:
|
||||||
pub struct SchemaObject {
|
pub struct SchemaObject {
|
||||||
properties_: Vec<(FieldName, bool, Schema)>,
|
properties_: Vec<ObjectEntry>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SchemaObject {
|
impl SchemaObject {
|
||||||
@ -403,12 +416,12 @@ impl SchemaObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn properties_mut(&mut self) -> &mut [(FieldName, bool, Schema)] {
|
fn properties_mut(&mut self) -> &mut [ObjectEntry] {
|
||||||
&mut self.properties_
|
&mut self.properties_
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sort_properties(&mut self) {
|
fn sort_properties(&mut self) {
|
||||||
self.properties_.sort_by(|a, b| (a.0).cmp(&b.0));
|
self.properties_.sort_by(|a, b| (a.name).cmp(&b.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_extract_from(obj: &mut JSONObject) -> Result<Self, syn::Error> {
|
fn try_extract_from(obj: &mut JSONObject) -> Result<Self, syn::Error> {
|
||||||
@ -432,7 +445,7 @@ impl SchemaObject {
|
|||||||
.transpose()?
|
.transpose()?
|
||||||
.unwrap_or(false);
|
.unwrap_or(false);
|
||||||
|
|
||||||
properties.push((key, optional, schema.try_into()?));
|
properties.push(ObjectEntry::new(key, optional, schema.try_into()?));
|
||||||
|
|
||||||
Ok(properties)
|
Ok(properties)
|
||||||
},
|
},
|
||||||
@ -444,30 +457,32 @@ impl SchemaObject {
|
|||||||
|
|
||||||
fn to_schema_inner(&self, ts: &mut TokenStream) -> Result<(), Error> {
|
fn to_schema_inner(&self, ts: &mut TokenStream) -> Result<(), Error> {
|
||||||
for element in self.properties_.iter() {
|
for element in self.properties_.iter() {
|
||||||
let key = element.0.as_str();
|
let key = element.name.as_str();
|
||||||
let optional = element.1;
|
let optional = element.optional;
|
||||||
let mut schema = TokenStream::new();
|
let mut schema = TokenStream::new();
|
||||||
element.2.to_schema(&mut schema)?;
|
element.schema.to_schema(&mut schema)?;
|
||||||
ts.extend(quote! { (#key, #optional, &#schema), });
|
ts.extend(quote! { (#key, #optional, &#schema), });
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_property_by_ident(&self, key: &str) -> Option<&(FieldName, bool, Schema)> {
|
fn find_property_by_ident(&self, key: &str) -> Option<&ObjectEntry> {
|
||||||
self.properties_.iter().find(|p| p.0.as_ident_str() == key)
|
self.properties_
|
||||||
|
.iter()
|
||||||
|
.find(|p| p.name.as_ident_str() == key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_property_by_ident_mut(&mut self, key: &str) -> Option<&mut (FieldName, bool, Schema)> {
|
fn find_property_by_ident_mut(&mut self, key: &str) -> Option<&mut ObjectEntry> {
|
||||||
self.properties_
|
self.properties_
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.find(|p| p.0.as_ident_str() == key)
|
.find(|p| p.name.as_ident_str() == key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_property_by_ident(&mut self, key: &str) -> bool {
|
fn remove_property_by_ident(&mut self, key: &str) -> bool {
|
||||||
match self
|
match self
|
||||||
.properties_
|
.properties_
|
||||||
.iter()
|
.iter()
|
||||||
.position(|(name, _, _)| name.as_ident_str() == key)
|
.position(|entry| entry.name.as_ident_str() == key)
|
||||||
{
|
{
|
||||||
Some(index) => {
|
Some(index) => {
|
||||||
self.properties_.remove(index);
|
self.properties_.remove(index);
|
||||||
@ -477,7 +492,7 @@ impl SchemaObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extend_properties(&mut self, new_fields: Vec<(FieldName, bool, Schema)>) {
|
fn extend_properties(&mut self, new_fields: Vec<ObjectEntry>) {
|
||||||
self.properties_.extend(new_fields);
|
self.properties_.extend(new_fields);
|
||||||
self.sort_properties();
|
self.sort_properties();
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ use proc_macro2::{Ident, Span, TokenStream};
|
|||||||
use quote::quote_spanned;
|
use quote::quote_spanned;
|
||||||
|
|
||||||
use super::Schema;
|
use super::Schema;
|
||||||
use crate::api::{self, SchemaItem};
|
use crate::api::{self, ObjectEntry, SchemaItem};
|
||||||
use crate::serde;
|
use crate::serde;
|
||||||
use crate::util::{self, FieldName, JSONObject, Maybe};
|
use crate::util::{self, FieldName, JSONObject, Maybe};
|
||||||
|
|
||||||
@ -126,19 +126,19 @@ fn handle_regular_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<T
|
|||||||
//
|
//
|
||||||
// NOTE: We remove references we're "done with" and in the end fail with a list of extraneous
|
// NOTE: We remove references we're "done with" and in the end fail with a list of extraneous
|
||||||
// fields if there are any.
|
// fields if there are any.
|
||||||
let mut schema_fields: HashMap<String, &mut (FieldName, bool, Schema)> = HashMap::new();
|
let mut schema_fields: HashMap<String, &mut ObjectEntry> = HashMap::new();
|
||||||
|
|
||||||
// We also keep a reference to the SchemaObject around since we derive missing fields
|
// We also keep a reference to the SchemaObject around since we derive missing fields
|
||||||
// automatically.
|
// automatically.
|
||||||
if let api::SchemaItem::Object(ref mut obj) = &mut schema.item {
|
if let api::SchemaItem::Object(ref mut obj) = &mut schema.item {
|
||||||
for field in obj.properties_mut() {
|
for field in obj.properties_mut() {
|
||||||
schema_fields.insert(field.0.as_str().to_string(), field);
|
schema_fields.insert(field.name.as_str().to_string(), field);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
error!(schema.span, "structs need an object schema");
|
error!(schema.span, "structs need an object schema");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut new_fields: Vec<(FieldName, bool, Schema)> = Vec::new();
|
let mut new_fields: Vec<ObjectEntry> = Vec::new();
|
||||||
|
|
||||||
let container_attrs = serde::ContainerAttrib::try_from(&stru.attrs[..])?;
|
let container_attrs = serde::ContainerAttrib::try_from(&stru.attrs[..])?;
|
||||||
|
|
||||||
@ -170,19 +170,19 @@ fn handle_regular_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<T
|
|||||||
if attrs.flatten {
|
if attrs.flatten {
|
||||||
to_remove.push(name.clone());
|
to_remove.push(name.clone());
|
||||||
|
|
||||||
let name = &field_def.0;
|
if field_def.schema.description.is_explicit() {
|
||||||
let optional = &field_def.1;
|
|
||||||
let schema = &field_def.2;
|
|
||||||
if schema.description.is_explicit() {
|
|
||||||
error!(
|
error!(
|
||||||
name.span(),
|
field_def.name.span(),
|
||||||
"flattened field should not have a description, \
|
"flattened field should not have a description, \
|
||||||
it does not appear in serialized data as a field",
|
it does not appear in serialized data as a field",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if *optional {
|
if field_def.optional {
|
||||||
error!(name.span(), "optional flattened fields are not supported");
|
error!(
|
||||||
|
field_def.name.span(),
|
||||||
|
"optional flattened fields are not supported"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +190,12 @@ fn handle_regular_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<T
|
|||||||
|
|
||||||
if attrs.flatten {
|
if attrs.flatten {
|
||||||
all_of_schemas.extend(quote::quote! {&});
|
all_of_schemas.extend(quote::quote! {&});
|
||||||
field_def.2.to_schema(&mut all_of_schemas)?;
|
field_def.schema.to_schema(&mut all_of_schemas)?;
|
||||||
all_of_schemas.extend(quote::quote! {,});
|
all_of_schemas.extend(quote::quote! {,});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let mut field_def = (
|
let mut field_def = ObjectEntry::new(
|
||||||
FieldName::new(name.clone(), span),
|
FieldName::new(name.clone(), span),
|
||||||
false,
|
false,
|
||||||
Schema::blank(span),
|
Schema::blank(span),
|
||||||
@ -204,7 +204,7 @@ fn handle_regular_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<T
|
|||||||
|
|
||||||
if attrs.flatten {
|
if attrs.flatten {
|
||||||
all_of_schemas.extend(quote::quote! {&});
|
all_of_schemas.extend(quote::quote! {&});
|
||||||
field_def.2.to_schema(&mut all_of_schemas)?;
|
field_def.schema.to_schema(&mut all_of_schemas)?;
|
||||||
all_of_schemas.extend(quote::quote! {,});
|
all_of_schemas.extend(quote::quote! {,});
|
||||||
to_remove.push(name.clone());
|
to_remove.push(name.clone());
|
||||||
} else {
|
} else {
|
||||||
@ -309,11 +309,11 @@ fn handle_regular_struct(attribs: JSONObject, stru: syn::ItemStruct) -> Result<T
|
|||||||
///
|
///
|
||||||
/// For each field we derive the description from doc-attributes if available.
|
/// For each field we derive the description from doc-attributes if available.
|
||||||
fn handle_regular_field(
|
fn handle_regular_field(
|
||||||
field_def: &mut (FieldName, bool, Schema),
|
field_def: &mut ObjectEntry,
|
||||||
field: &syn::Field,
|
field: &syn::Field,
|
||||||
derived: bool, // whether this field was missing in the schema
|
derived: bool, // whether this field was missing in the schema
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let schema: &mut Schema = &mut field_def.2;
|
let schema: &mut Schema = &mut field_def.schema;
|
||||||
|
|
||||||
if schema.description.is_none() {
|
if schema.description.is_none() {
|
||||||
let (doc_comment, doc_span) = util::get_doc_comments(&field.attrs)?;
|
let (doc_comment, doc_span) = util::get_doc_comments(&field.attrs)?;
|
||||||
@ -324,8 +324,8 @@ fn handle_regular_field(
|
|||||||
|
|
||||||
if is_option_type(&field.ty) {
|
if is_option_type(&field.ty) {
|
||||||
if derived {
|
if derived {
|
||||||
field_def.1 = true;
|
field_def.optional = true;
|
||||||
} else if !field_def.1 {
|
} else if !field_def.optional {
|
||||||
error!(&field.ty => "non-optional Option type?");
|
error!(&field.ty => "non-optional Option type?");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user