mirror of
https://git.proxmox.com/git/proxmox
synced 2025-05-01 18:39:52 +00:00
macro: split enum handler into separate module
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
59d0c72642
commit
ed503f689d
@ -1,15 +1,12 @@
|
|||||||
use std::mem;
|
use proc_macro2::{Delimiter, TokenStream, TokenTree};
|
||||||
|
|
||||||
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
|
||||||
|
|
||||||
use failure::Error;
|
use failure::Error;
|
||||||
use quote::{quote_spanned, ToTokens};
|
use quote::ToTokens;
|
||||||
use syn::spanned::Spanned;
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
use crate::api_def::{CommonTypeDefinition, ParameterDefinition};
|
use crate::parsing::parse_object;
|
||||||
use crate::parsing::*;
|
|
||||||
use crate::util;
|
|
||||||
|
|
||||||
|
mod enum_types;
|
||||||
mod function;
|
mod function;
|
||||||
mod struct_types;
|
mod struct_types;
|
||||||
|
|
||||||
@ -40,7 +37,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
|
|||||||
}
|
}
|
||||||
syn::Item::Fn(func) => function::handle_function(def_span, definition, func),
|
syn::Item::Fn(func) => function::handle_function(def_span, definition, func),
|
||||||
syn::Item::Enum(ref mut itemenum) => {
|
syn::Item::Enum(ref mut itemenum) => {
|
||||||
let extra = handle_enum(definition, itemenum)?;
|
let extra = enum_types::handle_enum(definition, itemenum)?;
|
||||||
let mut output = item.into_token_stream();
|
let mut output = item.into_token_stream();
|
||||||
output.extend(extra);
|
output.extend(extra);
|
||||||
Ok(output)
|
Ok(output)
|
||||||
@ -48,112 +45,3 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
|
|||||||
_ => c_bail!(item => "api macro currently only applies to structs and functions"),
|
_ => c_bail!(item => "api macro currently only applies to structs and functions"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Enums are string types. Note that we usually use lower case enum values, but rust wants
|
|
||||||
/// CamelCase, so unless otherwise requested by the user (todo!), we convert CamelCase to
|
|
||||||
/// underscore_case automatically.
|
|
||||||
///
|
|
||||||
/// For enums we automatically implement `ToString`, `FromStr`, and derive `Serialize` and
|
|
||||||
/// `Deserialize` via `serde_plain`.
|
|
||||||
fn handle_enum(mut definition: Object, item: &mut syn::ItemEnum) -> Result<TokenStream, Error> {
|
|
||||||
if item.generics.lt_token.is_some() {
|
|
||||||
c_bail!(
|
|
||||||
item.generics.span(),
|
|
||||||
"generic types are currently not supported"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let enum_ident = &item.ident;
|
|
||||||
let enum_name = enum_ident.to_string();
|
|
||||||
let expected = format!("valid {}", enum_ident);
|
|
||||||
|
|
||||||
let mut display_entries = TokenStream::new();
|
|
||||||
let mut from_str_entries = TokenStream::new();
|
|
||||||
|
|
||||||
for variant in item.variants.iter_mut() {
|
|
||||||
if variant.fields != syn::Fields::Unit {
|
|
||||||
c_bail!(variant.span(), "#[api] enums cannot have fields");
|
|
||||||
}
|
|
||||||
|
|
||||||
let variant_ident = &variant.ident;
|
|
||||||
let span = variant_ident.span();
|
|
||||||
let underscore_name = util::to_underscore_case(&variant_ident.to_string());
|
|
||||||
let mut underscore_name = syn::LitStr::new(&underscore_name, variant_ident.span());
|
|
||||||
|
|
||||||
let cap = variant.attrs.len();
|
|
||||||
for attr in mem::replace(&mut variant.attrs, Vec::with_capacity(cap)) {
|
|
||||||
if attr.path.is_ident(Ident::new("api", Span::call_site())) {
|
|
||||||
use util::ApiItem;
|
|
||||||
|
|
||||||
let attrs: util::ApiAttr = syn::parse2(attr.tts)?;
|
|
||||||
|
|
||||||
for attr in attrs.items {
|
|
||||||
match attr {
|
|
||||||
ApiItem::Rename(to) => underscore_name = to,
|
|
||||||
//other => c_bail!(other.span(), "unsupported attribute on enum variant"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
variant.attrs.push(attr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
display_entries.extend(quote_spanned! {
|
|
||||||
span => #enum_ident::#variant_ident => write!(f, #underscore_name),
|
|
||||||
});
|
|
||||||
|
|
||||||
from_str_entries.extend(quote_spanned! {
|
|
||||||
span => #underscore_name => Ok(#enum_ident::#variant_ident),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let common = CommonTypeDefinition::from_object(&mut definition)?;
|
|
||||||
let apidef = ParameterDefinition::from_object(definition)?;
|
|
||||||
|
|
||||||
if let Some(validate) = apidef.validate {
|
|
||||||
c_bail!(validate => "validators are not allowed on enum types");
|
|
||||||
}
|
|
||||||
|
|
||||||
let description = common.description;
|
|
||||||
let parse_cli = common.cli.quote(&enum_ident);
|
|
||||||
Ok(quote_spanned! { item.span() =>
|
|
||||||
impl ::std::fmt::Display for #enum_ident {
|
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
#display_entries
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::str::FromStr for #enum_ident {
|
|
||||||
type Err = ::failure::Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
|
|
||||||
match s {
|
|
||||||
#from_str_entries
|
|
||||||
_ => ::failure::bail!("expected {}", #expected),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
::serde_plain::derive_deserialize_from_str!(#enum_ident, #expected);
|
|
||||||
::serde_plain::derive_serialize_from_display!(#enum_ident);
|
|
||||||
::proxmox::api::derive_parse_cli_from_str!(#enum_ident);
|
|
||||||
|
|
||||||
impl ::proxmox::api::ApiType for #enum_ident {
|
|
||||||
fn type_info() -> &'static ::proxmox::api::TypeInfo {
|
|
||||||
const INFO: ::proxmox::api::TypeInfo = ::proxmox::api::TypeInfo {
|
|
||||||
name: #enum_name,
|
|
||||||
description: #description,
|
|
||||||
complete_fn: None, // FIXME!
|
|
||||||
parse_cli: #parse_cli,
|
|
||||||
};
|
|
||||||
&INFO
|
|
||||||
}
|
|
||||||
|
|
||||||
fn verify(&self) -> ::std::result::Result<(), ::failure::Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
124
proxmox-api-macro/src/api_macro/enum_types.rs
Normal file
124
proxmox-api-macro/src/api_macro/enum_types.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
//! `#[api]` handler for enums.
|
||||||
|
//!
|
||||||
|
//! Simple enums without data are string types. Note that we usually use lower case enum values,
|
||||||
|
//! but rust wants CamelCase, so unless otherwise requested by the user, we convert `CamelCase` to
|
||||||
|
//! `underscore_case` automatically.
|
||||||
|
//!
|
||||||
|
//! For "string" enums we automatically implement `ToString`, `FromStr`, and derive `Serialize` and
|
||||||
|
//! `Deserialize` via `serde_plain`.
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
use proc_macro2::{Ident, Span, TokenStream};
|
||||||
|
|
||||||
|
use failure::Error;
|
||||||
|
use quote::quote_spanned;
|
||||||
|
use syn::spanned::Spanned;
|
||||||
|
|
||||||
|
use crate::api_def::{CommonTypeDefinition, ParameterDefinition};
|
||||||
|
use crate::parsing::Object;
|
||||||
|
|
||||||
|
use crate::util;
|
||||||
|
|
||||||
|
pub fn handle_enum(mut definition: Object, item: &mut syn::ItemEnum) -> Result<TokenStream, Error> {
|
||||||
|
if item.generics.lt_token.is_some() {
|
||||||
|
c_bail!(
|
||||||
|
item.generics.span(),
|
||||||
|
"generic types are currently not supported"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let enum_ident = &item.ident;
|
||||||
|
let enum_name = enum_ident.to_string();
|
||||||
|
let expected = format!("valid {}", enum_ident);
|
||||||
|
|
||||||
|
let mut display_entries = TokenStream::new();
|
||||||
|
let mut from_str_entries = TokenStream::new();
|
||||||
|
|
||||||
|
for variant in item.variants.iter_mut() {
|
||||||
|
if variant.fields != syn::Fields::Unit {
|
||||||
|
c_bail!(variant.span(), "#[api] enums cannot have fields");
|
||||||
|
}
|
||||||
|
|
||||||
|
let variant_ident = &variant.ident;
|
||||||
|
let span = variant_ident.span();
|
||||||
|
let underscore_name = util::to_underscore_case(&variant_ident.to_string());
|
||||||
|
let mut underscore_name = syn::LitStr::new(&underscore_name, variant_ident.span());
|
||||||
|
|
||||||
|
let cap = variant.attrs.len();
|
||||||
|
for attr in mem::replace(&mut variant.attrs, Vec::with_capacity(cap)) {
|
||||||
|
if attr.path.is_ident(Ident::new("api", Span::call_site())) {
|
||||||
|
use util::ApiItem;
|
||||||
|
|
||||||
|
let attrs: util::ApiAttr = syn::parse2(attr.tts)?;
|
||||||
|
|
||||||
|
for attr in attrs.items {
|
||||||
|
match attr {
|
||||||
|
ApiItem::Rename(to) => underscore_name = to,
|
||||||
|
//other => c_bail!(other.span(), "unsupported attribute on enum variant"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
variant.attrs.push(attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display_entries.extend(quote_spanned! {
|
||||||
|
span => #enum_ident::#variant_ident => write!(f, #underscore_name),
|
||||||
|
});
|
||||||
|
|
||||||
|
from_str_entries.extend(quote_spanned! {
|
||||||
|
span => #underscore_name => Ok(#enum_ident::#variant_ident),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let common = CommonTypeDefinition::from_object(&mut definition)?;
|
||||||
|
let apidef = ParameterDefinition::from_object(definition)?;
|
||||||
|
|
||||||
|
if let Some(validate) = apidef.validate {
|
||||||
|
c_bail!(validate => "validators are not allowed on enum types");
|
||||||
|
}
|
||||||
|
|
||||||
|
let description = common.description;
|
||||||
|
let parse_cli = common.cli.quote(&enum_ident);
|
||||||
|
Ok(quote_spanned! { item.span() =>
|
||||||
|
impl ::std::fmt::Display for #enum_ident {
|
||||||
|
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
#display_entries
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ::std::str::FromStr for #enum_ident {
|
||||||
|
type Err = ::failure::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
#from_str_entries
|
||||||
|
_ => ::failure::bail!("expected {}", #expected),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
::serde_plain::derive_deserialize_from_str!(#enum_ident, #expected);
|
||||||
|
::serde_plain::derive_serialize_from_display!(#enum_ident);
|
||||||
|
::proxmox::api::derive_parse_cli_from_str!(#enum_ident);
|
||||||
|
|
||||||
|
impl ::proxmox::api::ApiType for #enum_ident {
|
||||||
|
fn type_info() -> &'static ::proxmox::api::TypeInfo {
|
||||||
|
const INFO: ::proxmox::api::TypeInfo = ::proxmox::api::TypeInfo {
|
||||||
|
name: #enum_name,
|
||||||
|
description: #description,
|
||||||
|
complete_fn: None, // FIXME!
|
||||||
|
parse_cli: #parse_cli,
|
||||||
|
};
|
||||||
|
&INFO
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(&self) -> ::std::result::Result<(), ::failure::Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
@ -16,7 +16,7 @@ mod newtype;
|
|||||||
mod unnamed;
|
mod unnamed;
|
||||||
|
|
||||||
/// Commonly used items of a struct field.
|
/// Commonly used items of a struct field.
|
||||||
struct StructField<'i, 't> {
|
pub struct StructField<'i, 't> {
|
||||||
def: ParameterDefinition,
|
def: ParameterDefinition,
|
||||||
ident: Option<&'i Ident>,
|
ident: Option<&'i Ident>,
|
||||||
access: syn::Member,
|
access: syn::Member,
|
||||||
|
Loading…
Reference in New Issue
Block a user