public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Stefan Hanreich <s.hanreich@proxmox.com>
To: pbs-devel@lists.proxmox.com
Subject: [pbs-devel] [PATCH proxmox 4/5] pve-api-types: add proxmox-fixed-string
Date: Thu, 20 Nov 2025 15:50:12 +0100	[thread overview]
Message-ID: <20251120145031.550340-5-s.hanreich@proxmox.com> (raw)
In-Reply-To: <20251120145031.550340-1-s.hanreich@proxmox.com>

The FixedString type has been moved into its own crate, so it can be
shared with pbs-api-types. Delete the existing implementation and use
the proxmox-fixed-string crate instead. No functional changes
intended.

Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
 pve-api-types/Cargo.toml                |   1 +
 pve-api-types/debian/control            |   2 +
 pve-api-types/src/types/fixed_string.rs | 274 ------------------------
 pve-api-types/src/types/mod.rs          |   3 +-
 4 files changed, 4 insertions(+), 276 deletions(-)
 delete mode 100644 pve-api-types/src/types/fixed_string.rs

diff --git a/pve-api-types/Cargo.toml b/pve-api-types/Cargo.toml
index 0088777a..98671079 100644
--- a/pve-api-types/Cargo.toml
+++ b/pve-api-types/Cargo.toml
@@ -22,6 +22,7 @@ serde_json.workspace = true
 serde_plain.workspace = true
 #
 proxmox-api-macro.workspace = true
+proxmox-fixed-string.workspace = true
 proxmox-serde = { workspace = true, features = [ "perl" ] }
 proxmox-schema = { workspace = true, features = [ "api-types", "api-macro" ] }
 
diff --git a/pve-api-types/debian/control b/pve-api-types/debian/control
index 5e9cdbc1..ca45b5ab 100644
--- a/pve-api-types/debian/control
+++ b/pve-api-types/debian/control
@@ -9,6 +9,7 @@ Build-Depends-Arch: cargo:native <!nocheck>,
  librust-anyhow-1+default-dev <!nocheck>,
  librust-async-trait-0.1+default-dev <!nocheck>,
  librust-proxmox-api-macro-1+default-dev (>= 1.4.3-~~) <!nocheck>,
+ librust-proxmox-fixed-string-0.1+default-dev <!nocheck>,
  librust-proxmox-schema-5+api-macro-dev (>= 5.0.1-~~) <!nocheck>,
  librust-proxmox-schema-5+api-types-dev (>= 5.0.1-~~) <!nocheck>,
  librust-proxmox-schema-5+default-dev (>= 5.0.1-~~) <!nocheck>,
@@ -35,6 +36,7 @@ Depends:
  librust-anyhow-1+default-dev,
  librust-async-trait-0.1+default-dev,
  librust-proxmox-api-macro-1+default-dev (>= 1.4.3-~~),
+ librust-proxmox-fixed-string-0.1+default-dev,
  librust-proxmox-schema-5+api-macro-dev (>= 5.0.1-~~),
  librust-proxmox-schema-5+api-types-dev (>= 5.0.1-~~),
  librust-proxmox-schema-5+default-dev (>= 5.0.1-~~),
diff --git a/pve-api-types/src/types/fixed_string.rs b/pve-api-types/src/types/fixed_string.rs
deleted file mode 100644
index e70e1327..00000000
--- a/pve-api-types/src/types/fixed_string.rs
+++ /dev/null
@@ -1,274 +0,0 @@
-use std::borrow::Borrow;
-use std::cmp::Ordering;
-use std::error::Error;
-use std::fmt;
-use std::ops::Deref;
-use std::str::FromStr;
-
-use serde::{Deserialize, Serialize};
-
-/// Error type used by constructors of [`FixedString`]
-#[derive(Clone, Copy, Debug)]
-pub struct TooLongError;
-
-impl Error for TooLongError {}
-
-impl fmt::Display for TooLongError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        f.write_str("string is longer than 23 characters")
-    }
-}
-
-/// An immutable string type with a maximum size of 23 bytes.
-///
-/// After construction it is guaranteed that its contents are:
-/// * valid utf-8
-/// * not longer than 23 characters
-///
-/// FixedString is immutable, therefore it is sufficient to validate the invariants only at
-/// construction time to guarantee that they will always hold during the lifecycle of the
-/// struct.
-#[derive(Clone, Copy)]
-pub struct FixedString {
-    buf: [u8; 23],
-    len: u8,
-}
-
-impl FixedString {
-    /// Creates a new FixedString instance from a str reference.
-    ///
-    /// # Errors
-    /// This function will return an error if:
-    /// * The passed string is longer than 23 bytes
-    pub fn new(value: &str) -> Result<Self, TooLongError> {
-        if value.len() > 23 {
-            return Err(TooLongError);
-        }
-
-        let mut buf = [0; 23];
-        buf[..value.len()].copy_from_slice(value.as_bytes());
-
-        Ok(Self {
-            buf,
-            // SAFETY: self.len is at least 0 and at most 23, which fits into u8
-            len: value.len() as u8,
-        })
-    }
-
-    /// Returns a str reference to the stored data
-    #[inline]
-    pub fn as_str(&self) -> &str {
-        // SAFETY: self.buf must be a valid utf-8 string by construction
-        unsafe { str::from_utf8_unchecked(self.as_bytes()) }
-    }
-
-    /// Returns a reference to the set bytes in the stored buffer
-    #[inline]
-    pub fn as_bytes(&self) -> &[u8] {
-        // SAFETY: self.len >= 0 and self.len <= 23 by construction
-        unsafe { self.buf.get_unchecked(..self.len as usize) }
-    }
-}
-
-macro_rules! forward_impl_to_bytes {
-    ($($trait:ident {$fn:ident -> $out:ty })+) => {
-        $(
-            impl $trait for FixedString {
-                #[inline]
-                fn $fn(&self, other: &Self) -> $out {
-                    <[u8] as $trait>::$fn(self.as_bytes(), other.as_bytes())
-                }
-            }
-        )+
-    };
-}
-
-forward_impl_to_bytes! {
-    PartialEq { eq -> bool }
-    PartialOrd { partial_cmp -> Option<Ordering> }
-    Ord { cmp -> Ordering }
-}
-
-macro_rules! forward_impl_to_str_bidir {
-    ($($trait:ident {$fn:ident -> $out:ty })+) => {
-        $(
-            impl $trait<str> for FixedString {
-                #[inline]
-                fn $fn(&self, other: &str) -> $out {
-                    <str as $trait>::$fn(self.as_str(), other)
-                }
-            }
-
-            impl $trait<FixedString> for &str {
-                #[inline]
-                fn $fn(&self, other: &FixedString) -> $out {
-                    <str as $trait>::$fn(self, other.as_str())
-                }
-            }
-        )+
-    };
-}
-
-forward_impl_to_str_bidir! {
-    PartialEq { eq -> bool }
-    PartialOrd { partial_cmp -> Option<Ordering> }
-}
-
-impl Eq for FixedString {}
-
-impl fmt::Display for FixedString {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-
-impl fmt::Debug for FixedString {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        fmt::Display::fmt(self.as_str(), f)
-    }
-}
-
-impl Deref for FixedString {
-    type Target = str;
-
-    fn deref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl AsRef<str> for FixedString {
-    fn as_ref(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl AsRef<[u8]> for FixedString {
-    fn as_ref(&self) -> &[u8] {
-        self.as_bytes()
-    }
-}
-
-impl Borrow<str> for FixedString {
-    fn borrow(&self) -> &str {
-        self.as_str()
-    }
-}
-
-impl TryFrom<String> for FixedString {
-    type Error = TooLongError;
-
-    fn try_from(value: String) -> Result<Self, Self::Error> {
-        FixedString::new(value.as_str())
-    }
-}
-
-impl FromStr for FixedString {
-    type Err = TooLongError;
-
-    fn from_str(value: &str) -> Result<Self, Self::Err> {
-        FixedString::new(value)
-    }
-}
-
-impl TryFrom<&str> for FixedString {
-    type Error = TooLongError;
-
-    fn try_from(value: &str) -> Result<Self, Self::Error> {
-        FixedString::new(value)
-    }
-}
-
-impl Serialize for FixedString {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: serde::Serializer,
-    {
-        serializer.serialize_str(self.as_str())
-    }
-}
-
-impl<'de> Deserialize<'de> for FixedString {
-    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
-    where
-        D: serde::Deserializer<'de>,
-    {
-        struct FixedStringVisitor;
-
-        impl<'de> serde::de::Visitor<'de> for FixedStringVisitor {
-            type Value = FixedString;
-
-            fn expecting(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
-                f.write_str("a string that is at most 23 bytes long")
-            }
-
-            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
-            where
-                E: serde::de::Error,
-            {
-                v.try_into().map_err(E::custom)
-            }
-        }
-
-        deserializer.deserialize_str(FixedStringVisitor)
-    }
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    use serde_plain;
-
-    #[test]
-    fn test_construct() {
-        let fixed_string = FixedString::new("").expect("empty string is valid");
-        assert_eq!("", fixed_string);
-
-        let fixed_string = FixedString::new("a").expect("valid string");
-        assert_eq!("a", fixed_string);
-
-        let fixed_string = FixedString::new("🌏🌏🌏🌏🌏").expect("valid string");
-        assert_eq!("🌏🌏🌏🌏🌏", fixed_string);
-
-        let fixed_string =
-            FixedString::new("aaaaaaaaaaaaaaaaaaaaaaa").expect("23 characters are allowed");
-        assert_eq!("aaaaaaaaaaaaaaaaaaaaaaa", fixed_string);
-
-        FixedString::new("🌏🌏🌏🌏🌏🌏").expect_err("string too long");
-        FixedString::new("aaaaaaaaaaaaaaaaaaaaaaaa").expect_err("string too long");
-    }
-
-    #[test]
-    fn test_serialize_deserialize() {
-        let valid_string = "aaaaaaaaaaaaaaaaaaaaaaa";
-
-        let fixed_string: FixedString =
-            serde_plain::from_str("aaaaaaaaaaaaaaaaaaaaaaa").expect("deserialization works");
-        assert_eq!(valid_string, fixed_string);
-
-        let serialized_string =
-            serde_plain::to_string(&fixed_string).expect("can be serialized into a string");
-        assert_eq!(valid_string, serialized_string);
-
-        serde_plain::from_str::<FixedString>("aaaaaaaaaaaaaaaaaaaaaaaa")
-            .expect_err("cannot deserialize string that is too long");
-    }
-
-    #[test]
-    fn test_ord() {
-        let fixed_string = FixedString::new("abc").expect("valid string");
-
-        assert!(fixed_string == fixed_string);
-        assert!(fixed_string >= fixed_string);
-        assert!(fixed_string <= fixed_string);
-
-        assert!("ab" < fixed_string);
-        assert!("abc" == fixed_string);
-        assert!("abcd" > fixed_string);
-
-        let larger_fixed_string = FixedString::new("abcde").expect("valid string");
-
-        assert!(larger_fixed_string > fixed_string);
-        assert!(fixed_string < larger_fixed_string);
-    }
-}
diff --git a/pve-api-types/src/types/mod.rs b/pve-api-types/src/types/mod.rs
index 63209d84..e5eed817 100644
--- a/pve-api-types/src/types/mod.rs
+++ b/pve-api-types/src/types/mod.rs
@@ -17,8 +17,7 @@ pub mod array;
 pub mod stringlist;
 pub mod verifiers;
 
-mod fixed_string;
-pub use fixed_string::{FixedString, TooLongError};
+use proxmox_fixed_string::FixedString;
 
 include!("../generated/types.rs");
 
-- 
2.47.3


_______________________________________________
pbs-devel mailing list
pbs-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel

  parent reply	other threads:[~2025-11-20 14:51 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-20 14:50 [pbs-devel] [RFC proxmox{, -datacenter-manager} 0/6] Add fallback variants to PBS API types Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 1/5] proxmox-upgrade-checks: fix meta package version check Stefan Hanreich
2025-11-20 15:04   ` Shannon Sterz
2025-11-20 15:08     ` Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 2/5] proxmox-fixed-string: extract FixedString into own crate Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 3/5] proxmox-fixed-string: implement hash trait Stefan Hanreich
2025-11-20 14:50 ` Stefan Hanreich [this message]
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox 5/5] pbs-api-types: add fallback variants to enums in public API Stefan Hanreich
2025-11-20 14:50 ` [pbs-devel] [PATCH proxmox-datacenter-manager 1/1] tree-wide: add enum fallback variants for pbs api types Stefan Hanreich

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251120145031.550340-5-s.hanreich@proxmox.com \
    --to=s.hanreich@proxmox.com \
    --cc=pbs-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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