public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox] add api_string_type macro
@ 2021-05-04 12:41 Wolfgang Bumiller
  2021-05-05  5:48 ` [pbs-devel] applied: " Dietmar Maurer
  0 siblings, 1 reply; 2+ messages in thread
From: Wolfgang Bumiller @ 2021-05-04 12:41 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
Difference to the original RFC for the proxmox-backup crate:
* dropped `Debug` implementation
* added `TryFrom<String>` implementation

 proxmox/src/api/api_type_macros.rs | 113 +++++++++++++++++++++++++++++
 proxmox/src/api/mod.rs             |   3 +
 2 files changed, 116 insertions(+)
 create mode 100644 proxmox/src/api/api_type_macros.rs

diff --git a/proxmox/src/api/api_type_macros.rs b/proxmox/src/api/api_type_macros.rs
new file mode 100644
index 0000000..542520d
--- /dev/null
+++ b/proxmox/src/api/api_type_macros.rs
@@ -0,0 +1,113 @@
+/// Helper macro to generate a simple string type wrapper.
+///
+/// This is meant to be used with an API-type tuple struct containing a single `String` like this:
+///
+/// ```
+/// # use proxmox::api::api;
+/// # use proxmox::api::schema::ApiStringFormat;
+/// # const PROXMOX_SAFE_ID_FORMAT: ApiStringFormat = ApiStringFormat::Enum(&[]);
+/// use proxmox::api_string_type;
+/// use serde::{Deserialize, Serialize};
+///
+/// api_string_type! {
+///     #[api(format: &PROXMOX_SAFE_ID_FORMAT)]
+///     /// ACME account name.
+///     #[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
+///     #[serde(transparent)]
+///     pub struct AccountName(String);
+/// }
+/// ```
+///
+/// This will automatically implements:
+/// * `Display` as a pass-through to `String`'s `Display`
+/// * `Deref`
+/// * `DerefMut`
+/// * `AsRef<str>`
+/// * `TryFrom<String>`
+/// * `fn into_string(self) -> String`
+/// * `fn as_str(&self) -> &str`
+/// * `fn from_string(inner: String) -> Result<Self, anyhow::Error>` using
+///   `StringSchema::check_constraints`.
+/// * `unsafe fn from_string_unchecked(inner: String) -> Self`
+#[macro_export]
+macro_rules! api_string_type {
+    (
+        $(#[$doc:meta])*
+        $vis:vis struct $name:ident(String);
+    ) => (
+        $(#[$doc])*
+        $vis struct $name(String);
+
+        impl ::std::ops::Deref for $name {
+            type Target = str;
+
+            #[inline]
+            fn deref(&self) -> &str {
+                &self.0
+            }
+        }
+
+        impl ::std::ops::DerefMut for $name {
+            #[inline]
+            fn deref_mut(&mut self) -> &mut str {
+                &mut self.0
+            }
+        }
+
+        impl AsRef<str> for $name {
+            #[inline]
+            fn as_ref(&self) -> &str {
+                self.0.as_ref()
+            }
+        }
+
+        impl ::std::convert::TryFrom<String> for $name {
+            type Error = ::anyhow::Error;
+
+            fn try_from(inner: String) -> Result<Self, ::anyhow::Error> {
+                Self::from_string(inner)
+            }
+        }
+
+        impl $name {
+            /// Get the contained string.
+            pub fn into_string(self) -> String {
+                self.0
+            }
+
+            /// Get the string as slice.
+            pub fn as_str(&self) -> &str {
+                self.0.as_str()
+            }
+
+            /// Create an instance directly from a `String`.
+            ///
+            /// # Safety
+            ///
+            /// It is the caller's job to have validated the contents.
+            /// While there are no memory safety issues, a wrong string can cause API calls to
+            /// fail parameter validation.
+            pub unsafe fn from_string_unchecked(name: String) -> Self {
+                Self(name)
+            }
+
+            /// Create an instance directly from a `String`, validating it using the API schema's
+            /// [`check_constraints`](::proxmox::api::schema::StringSchema::check_constraints())
+            /// method.
+            pub fn from_string(inner: String) -> Result<Self, ::anyhow::Error> {
+                match &Self::API_SCHEMA {
+                    ::proxmox::api::schema::Schema::String(s) => s.check_constraints(&inner)?,
+                    _ => unreachable!(),
+                }
+                Ok(Self(inner))
+            }
+        }
+
+        impl ::std::fmt::Display for $name {
+            #[inline]
+            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                ::std::fmt::Display::fmt(&self.0, f)
+            }
+        }
+    );
+}
diff --git a/proxmox/src/api/mod.rs b/proxmox/src/api/mod.rs
index 8c6f597..aaec7df 100644
--- a/proxmox/src/api/mod.rs
+++ b/proxmox/src/api/mod.rs
@@ -9,6 +9,9 @@
 #[cfg(feature = "api-macro")]
 pub use proxmox_api_macro::{api, router};
 
+#[macro_use]
+mod api_type_macros;
+
 #[doc(hidden)]
 pub mod const_regex;
 #[doc(hidden)]
-- 
2.20.1





^ permalink raw reply	[flat|nested] 2+ messages in thread

* [pbs-devel] applied:  [PATCH proxmox] add api_string_type macro
  2021-05-04 12:41 [pbs-devel] [PATCH proxmox] add api_string_type macro Wolfgang Bumiller
@ 2021-05-05  5:48 ` Dietmar Maurer
  0 siblings, 0 replies; 2+ messages in thread
From: Dietmar Maurer @ 2021-05-05  5:48 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Wolfgang Bumiller

applied




^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2021-05-05  5:48 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-04 12:41 [pbs-devel] [PATCH proxmox] add api_string_type macro Wolfgang Bumiller
2021-05-05  5:48 ` [pbs-devel] applied: " Dietmar Maurer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal