]> git.proxmox.com Git - proxmox.git/commitdiff
macro: derive ser/de for newtypes just like structs
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 8 Aug 2019 09:29:12 +0000 (11:29 +0200)
committerWolfgang Bumiller <w.bumiller@proxmox.com>
Thu, 8 Aug 2019 09:29:12 +0000 (11:29 +0200)
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
api-test/src/schema/types/memory.rs
proxmox-api-macro/src/api_macro.rs
proxmox-api-macro/tests/basic.rs

index cfd72f2a99bc4081ffa5d4ed8302f4e2cddc43ff..b504199b36bdaff06dead24e9e87cb4fe17fa767 100644 (file)
@@ -8,6 +8,7 @@ use proxmox::api::api;
 // representations. Numeric always being bytes, string having suffixes.
 #[api({
     description: "Represents an amount of memory and can be expressed with suffixes such as MiB.",
+    serialize_as_string: true,
 })]
 #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug)]
 #[repr(transparent)]
@@ -40,7 +41,6 @@ impl std::str::FromStr for Memory {
         }
     }
 }
-serde_plain::derive_deserialize_from_str!(Memory, "valid memory amount description");
 proxmox::api::derive_parse_cli_from_str!(Memory);
 
 impl std::fmt::Display for Memory {
@@ -55,7 +55,6 @@ impl std::fmt::Display for Memory {
         write!(f, "{}{}", n, SUFFIXES[i])
     }
 }
-serde_plain::derive_serialize_from_display!(Memory);
 
 impl Memory {
     pub const fn from_bytes(v: u64) -> Self {
index 186ab00b0f6c3670012ce497e904f541e3170d61..e70969c3b57ee2c989bd8e3f3a022b7f870ebd08 100644 (file)
@@ -449,14 +449,25 @@ struct StructField<'i, 't> {
 
 fn handle_newtype(
     mut definition: Object,
-    name: &Ident,
+    type_ident: &Ident,
     item: &syn::FieldsUnnamed,
 ) -> Result<TokenStream, Error> {
+    let type_s = type_ident.to_string();
+    let type_span = type_ident.span();
+    let type_str = syn::LitStr::new(&type_s, type_span);
+
     let fields = &item.unnamed;
     let field_punct = fields.first().unwrap();
     let field = field_punct.value();
 
     let common = CommonTypeDefinition::from_object(&mut definition)?;
+
+    let serialize_as_string = definition
+        .remove("serialize_as_string")
+        .map(|e| e.expect_lit_bool_direct())
+        .transpose()?
+        .unwrap_or(false);
+
     let apidef = ParameterDefinition::from_object(definition)?;
 
     let impl_verify = struct_fields_impl_verify(item.span(), &[StructField {
@@ -464,23 +475,44 @@ fn handle_newtype(
         ident: None,
         access: syn::Member::Unnamed(syn::Index {
             index: 0,
-            span: name.span(),
+            span: type_ident.span(),
         }),
         mem_id: 0,
         string: "0".to_string(),
-        strlit: syn::LitStr::new("0", name.span()),
+        strlit: syn::LitStr::new("0", type_ident.span()),
         ty: &field.ty,
     }])?;
 
+    let (impl_serialize, impl_deserialize) = if serialize_as_string {
+        let expected = format!("valid {}", type_ident);
+        (
+            quote_spanned! { item.span() =>
+                ::serde_plain::derive_serialize_from_display!(#type_ident);
+            },
+            quote_spanned! { item.span() =>
+                ::serde_plain::derive_deserialize_from_str!(#type_ident, #expected);
+            },
+        )
+    } else {
+        (
+            newtype_derive_serialize(item.span(), type_ident),
+            newtype_derive_deserialize(item.span(), type_ident),
+        )
+    };
+
     let description = common.description;
-    let parse_cli = common.cli.quote(&name);
+    let parse_cli = common.cli.quote(&type_ident);
     Ok(quote! {
-        impl ::proxmox::api::ApiType for #name {
+        #impl_serialize
+
+        #impl_deserialize
+
+        impl ::proxmox::api::ApiType for #type_ident {
             fn type_info() -> &'static ::proxmox::api::TypeInfo {
                 use ::proxmox::api::cli::ParseCli;
                 use ::proxmox::api::cli::ParseCliFromStr;
                 const INFO: ::proxmox::api::TypeInfo = ::proxmox::api::TypeInfo {
-                    name: stringify!(#name),
+                    name: #type_str,
                     description: #description,
                     complete_fn: None, // FIXME!
                     parse_cli: #parse_cli,
@@ -493,6 +525,38 @@ fn handle_newtype(
     })
 }
 
+fn newtype_derive_serialize(
+    span: Span,
+    type_ident: &Ident,
+) -> TokenStream {
+    quote_spanned! { span =>
+        impl ::serde::ser::Serialize for #type_ident {
+            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
+            where
+                S: ::serde::ser::Serializer,
+            {
+                ::serde::ser::Serialize::serialize::<S>(&self.0, serializer)
+            }
+        }
+    }
+}
+
+fn newtype_derive_deserialize(
+    span: Span,
+    type_ident: &Ident,
+) -> TokenStream {
+    quote_spanned! { span =>
+        impl<'de> ::serde::de::Deserialize<'de> for #type_ident {
+            fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
+            where
+                D: ::serde::de::Deserializer<'de>,
+            {
+                Ok(Self(::serde::de::Deserialize::<'de>::deserialize::<D>(deserializer)?))
+            }
+        }
+    }
+}
+
 fn handle_struct_unnamed(
     mut definition: Object,
     name: &Ident,
index 1f2a796863195d3cf25caab681f72645e7416d6f..b1eb132ab0ef8d1f482336cbe1ee32ca30d6770c 100644 (file)
@@ -2,7 +2,6 @@
 
 use bytes::Bytes;
 use failure::{bail, Error};
-use serde_derive::{Deserialize, Serialize};
 use serde_json::Value;
 
 use proxmox::api::{api, Router};
@@ -11,7 +10,6 @@ use proxmox::api::{api, Router};
     description: "A hostname or IP address",
     validate: validate_hostname,
 })]
-#[derive(Deserialize, Serialize)]
 #[repr(transparent)]
 pub struct HostOrIp(String);