public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro
@ 2021-05-04 10:19 Wolfgang Bumiller
  2021-05-04 10:19 ` [pbs-devel] [RFC backup 2/2] use api_string_type macro Wolfgang Bumiller
  2021-05-04 11:17 ` [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro Dietmar Maurer
  0 siblings, 2 replies; 4+ messages in thread
From: Wolfgang Bumiller @ 2021-05-04 10:19 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 src/tools.rs                 |   3 +
 src/tools/api_string_type.rs | 105 +++++++++++++++++++++++++++++++++++
 2 files changed, 108 insertions(+)
 create mode 100644 src/tools/api_string_type.rs

diff --git a/src/tools.rs b/src/tools.rs
index c58569a6..98597c29 100644
--- a/src/tools.rs
+++ b/src/tools.rs
@@ -18,6 +18,9 @@ use percent_encoding::{utf8_percent_encode, AsciiSet};
 pub use proxmox::tools::fd::Fd;
 use proxmox::tools::fs::{create_path, CreateOptions};
 
+#[macro_use]
+pub mod api_string_type;
+
 pub mod acl;
 pub mod apt;
 pub mod async_io;
diff --git a/src/tools/api_string_type.rs b/src/tools/api_string_type.rs
new file mode 100644
index 00000000..122fcbed
--- /dev/null
+++ b/src/tools/api_string_type.rs
@@ -0,0 +1,105 @@
+/// 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:
+///
+/// ```ignore
+/// api_string_type! {
+///     #[api(format: &PROXMOX_SAFE_ID_FORMAT)]
+///     /// ACME account name.
+///     #[derive(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`
+/// * `Debug` as a pass-through to `String`'s `Debug`
+/// * `Deref`
+/// * `DerefMut`
+/// * `AsRef<str>`
+/// * `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_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 $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::Debug for $name {
+            #[inline]
+            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
+                ::std::fmt::Debug::fmt(&self.0, f)
+            }
+        }
+
+        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)
+            }
+        }
+    );
+}
-- 
2.20.1





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

* [pbs-devel] [RFC backup 2/2] use api_string_type macro
  2021-05-04 10:19 [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro Wolfgang Bumiller
@ 2021-05-04 10:19 ` Wolfgang Bumiller
  2021-05-05  6:33   ` [pbs-devel] applied: " Dietmar Maurer
  2021-05-04 11:17 ` [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro Dietmar Maurer
  1 sibling, 1 reply; 4+ messages in thread
From: Wolfgang Bumiller @ 2021-05-04 10:19 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
---
 src/api2/types/acme.rs | 68 ++++--------------------------------------
 1 file changed, 6 insertions(+), 62 deletions(-)

diff --git a/src/api2/types/acme.rs b/src/api2/types/acme.rs
index cc5df322..f66a0801 100644
--- a/src/api2/types/acme.rs
+++ b/src/api2/types/acme.rs
@@ -1,6 +1,3 @@
-use std::fmt;
-
-use anyhow::Error;
 use serde::{Deserialize, Serialize};
 
 use proxmox::api::{api, schema::{Schema, StringSchema, ApiStringFormat}};
@@ -64,63 +61,10 @@ pub struct KnownAcmeDirectory {
     pub url: &'static str,
 }
 
-#[api(format: &PROXMOX_SAFE_ID_FORMAT)]
-/// ACME account name.
-#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
-#[serde(transparent)]
-pub struct AcmeAccountName(String);
-
-impl AcmeAccountName {
-    pub fn into_string(self) -> String {
-        self.0
-    }
-
-    pub fn from_string(name: String) -> Result<Self, Error> {
-        match &Self::API_SCHEMA {
-            Schema::String(s) => s.check_constraints(&name)?,
-            _ => unreachable!(),
-        }
-        Ok(Self(name))
-    }
-
-    pub unsafe fn from_string_unchecked(name: String) -> Self {
-        Self(name)
-    }
-}
-
-impl std::ops::Deref for AcmeAccountName {
-    type Target = str;
-
-    #[inline]
-    fn deref(&self) -> &str {
-        &self.0
-    }
-}
-
-impl std::ops::DerefMut for AcmeAccountName {
-    #[inline]
-    fn deref_mut(&mut self) -> &mut str {
-        &mut self.0
-    }
-}
-
-impl AsRef<str> for AcmeAccountName {
-    #[inline]
-    fn as_ref(&self) -> &str {
-        self.0.as_ref()
-    }
-}
-
-impl fmt::Debug for AcmeAccountName {
-    #[inline]
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Debug::fmt(&self.0, f)
-    }
-}
-
-impl fmt::Display for AcmeAccountName {
-    #[inline]
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        fmt::Display::fmt(&self.0, f)
-    }
+api_string_type! {
+    #[api(format: &PROXMOX_SAFE_ID_FORMAT)]
+    /// ACME account name.
+    #[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
+    #[serde(transparent)]
+    pub struct AcmeAccountName(String);
 }
-- 
2.20.1





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

* Re: [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro
  2021-05-04 10:19 [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro Wolfgang Bumiller
  2021-05-04 10:19 ` [pbs-devel] [RFC backup 2/2] use api_string_type macro Wolfgang Bumiller
@ 2021-05-04 11:17 ` Dietmar Maurer
  1 sibling, 0 replies; 4+ messages in thread
From: Dietmar Maurer @ 2021-05-04 11:17 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Wolfgang Bumiller

two suggestions:

1.) Move the code to the proxmox crate

2.) Do not implement Debug (can be derived)


On 5/4/21 12:19 PM, Wolfgang Bumiller wrote:
> Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
> ---
>   src/tools.rs                 |   3 +
>   src/tools/api_string_type.rs | 105 +++++++++++++++++++++++++++++++++++
>   2 files changed, 108 insertions(+)
>   create mode 100644 src/tools/api_string_type.rs
>
> diff --git a/src/tools.rs b/src/tools.rs
> index c58569a6..98597c29 100644
> --- a/src/tools.rs
> +++ b/src/tools.rs
> @@ -18,6 +18,9 @@ use percent_encoding::{utf8_percent_encode, AsciiSet};
>   pub use proxmox::tools::fd::Fd;
>   use proxmox::tools::fs::{create_path, CreateOptions};
>   
> +#[macro_use]
> +pub mod api_string_type;
> +
>   pub mod acl;
>   pub mod apt;
>   pub mod async_io;
> diff --git a/src/tools/api_string_type.rs b/src/tools/api_string_type.rs
> new file mode 100644
> index 00000000..122fcbed
> --- /dev/null
> +++ b/src/tools/api_string_type.rs
> @@ -0,0 +1,105 @@
> +/// 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:
> +///
> +/// ```ignore
> +/// api_string_type! {
> +///     #[api(format: &PROXMOX_SAFE_ID_FORMAT)]
> +///     /// ACME account name.
> +///     #[derive(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`
> +/// * `Debug` as a pass-through to `String`'s `Debug`
> +/// * `Deref`
> +/// * `DerefMut`
> +/// * `AsRef<str>`
> +/// * `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_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 $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::Debug for $name {
> +            #[inline]
> +            fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
> +                ::std::fmt::Debug::fmt(&self.0, f)
> +            }
> +        }
> +
> +        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)
> +            }
> +        }
> +    );
> +}




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

* [pbs-devel] applied:  [RFC backup 2/2] use api_string_type macro
  2021-05-04 10:19 ` [pbs-devel] [RFC backup 2/2] use api_string_type macro Wolfgang Bumiller
@ 2021-05-05  6:33   ` Dietmar Maurer
  0 siblings, 0 replies; 4+ messages in thread
From: Dietmar Maurer @ 2021-05-05  6:33 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Wolfgang Bumiller

applied, with minor modifications

On 5/4/21 12:19 PM, Wolfgang Bumiller wrote:
> Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
> ---
>   src/api2/types/acme.rs | 68 ++++--------------------------------------
>   1 file changed, 6 insertions(+), 62 deletions(-)
>
> diff --git a/src/api2/types/acme.rs b/src/api2/types/acme.rs
> index cc5df322..f66a0801 100644
> --- a/src/api2/types/acme.rs
> +++ b/src/api2/types/acme.rs
> @@ -1,6 +1,3 @@
> -use std::fmt;
> -
> -use anyhow::Error;
>   use serde::{Deserialize, Serialize};
>   
>   use proxmox::api::{api, schema::{Schema, StringSchema, ApiStringFormat}};
> @@ -64,63 +61,10 @@ pub struct KnownAcmeDirectory {
>       pub url: &'static str,
>   }
>   
> -#[api(format: &PROXMOX_SAFE_ID_FORMAT)]
> -/// ACME account name.
> -#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
> -#[serde(transparent)]
> -pub struct AcmeAccountName(String);
> -
> -impl AcmeAccountName {
> -    pub fn into_string(self) -> String {
> -        self.0
> -    }
> -
> -    pub fn from_string(name: String) -> Result<Self, Error> {
> -        match &Self::API_SCHEMA {
> -            Schema::String(s) => s.check_constraints(&name)?,
> -            _ => unreachable!(),
> -        }
> -        Ok(Self(name))
> -    }
> -
> -    pub unsafe fn from_string_unchecked(name: String) -> Self {
> -        Self(name)
> -    }
> -}
> -
> -impl std::ops::Deref for AcmeAccountName {
> -    type Target = str;
> -
> -    #[inline]
> -    fn deref(&self) -> &str {
> -        &self.0
> -    }
> -}
> -
> -impl std::ops::DerefMut for AcmeAccountName {
> -    #[inline]
> -    fn deref_mut(&mut self) -> &mut str {
> -        &mut self.0
> -    }
> -}
> -
> -impl AsRef<str> for AcmeAccountName {
> -    #[inline]
> -    fn as_ref(&self) -> &str {
> -        self.0.as_ref()
> -    }
> -}
> -
> -impl fmt::Debug for AcmeAccountName {
> -    #[inline]
> -    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
> -        fmt::Debug::fmt(&self.0, f)
> -    }
> -}
> -
> -impl fmt::Display for AcmeAccountName {
> -    #[inline]
> -    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
> -        fmt::Display::fmt(&self.0, f)
> -    }
> +api_string_type! {
> +    #[api(format: &PROXMOX_SAFE_ID_FORMAT)]
> +    /// ACME account name.
> +    #[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
> +    #[serde(transparent)]
> +    pub struct AcmeAccountName(String);
>   }




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

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

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-04 10:19 [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro Wolfgang Bumiller
2021-05-04 10:19 ` [pbs-devel] [RFC backup 2/2] use api_string_type macro Wolfgang Bumiller
2021-05-05  6:33   ` [pbs-devel] applied: " Dietmar Maurer
2021-05-04 11:17 ` [pbs-devel] [RFC backup 1/2] implement an api_string_type helper macro 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