From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH proxmox 02/11] serde: add base64 module for byte arrays
Date: Fri, 16 Jan 2026 16:33:07 +0100 [thread overview]
Message-ID: <20260116153317.1146323-3-c.heiss@proxmox.com> (raw)
In-Reply-To: <20260116153317.1146323-1-c.heiss@proxmox.com>
Allows to directly en-/decode [u8; N] to/from a base64 string, much like
the already existing bytes_as_base64 allows for Vec<u8>.
Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
proxmox-serde/src/lib.rs | 91 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 91 insertions(+)
diff --git a/proxmox-serde/src/lib.rs b/proxmox-serde/src/lib.rs
index 28c3054d..c3ec4f41 100644
--- a/proxmox-serde/src/lib.rs
+++ b/proxmox-serde/src/lib.rs
@@ -159,3 +159,94 @@ pub mod string_as_base64 {
<T as StrAsBase64>::de::<'de, D>(deserializer)
}
}
+
+/// Serialize `[u8; N]` or `Option<[u8; N]>` as base64 encoded.
+///
+/// If you do not need the convenience of handling both [u8; N] and Option transparently, you could
+/// also use [`proxmox_base64`] directly.
+///
+/// Usage example:
+/// ```
+/// use serde::{Deserialize, Serialize};
+///
+/// #[derive(Debug, Deserialize, PartialEq, Serialize)]
+/// struct Foo {
+/// #[serde(with = "proxmox_serde::byte_array_as_base64")]
+/// data: [u8; 4],
+/// }
+///
+/// let obj = Foo { data: [1, 2, 3, 4] };
+/// let json = serde_json::to_string(&obj).unwrap();
+/// assert_eq!(json, r#"{"data":"AQIDBA=="}"#);
+///
+/// let deserialized: Foo = serde_json::from_str(&json).unwrap();
+/// assert_eq!(obj, deserialized);
+/// ```
+pub mod byte_array_as_base64 {
+ use serde::{Deserialize, Deserializer, Serializer};
+
+ /// Private trait to enable `byte_array_as_base64` for `Option<[u8; N]>` in addition to `[u8; N]`.
+ #[doc(hidden)]
+ pub trait ByteArrayAsBase64<const N: usize>: Sized {
+ fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>;
+ fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error>;
+ }
+
+ fn finish_deserializing<'de, const N: usize, D: Deserializer<'de>>(
+ string: String,
+ ) -> Result<[u8; N], D::Error> {
+ use serde::de::Error;
+
+ let vec = proxmox_base64::decode(string).map_err(|err| {
+ let msg = format!("base64 decode: {}", err);
+ Error::custom(msg)
+ })?;
+
+ vec.as_slice().try_into().map_err(|_| {
+ let msg = format!("expected {N} bytes, got {}", vec.len());
+ Error::custom(msg)
+ })
+ }
+
+ impl<const N: usize> ByteArrayAsBase64<N> for [u8; N] {
+ fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ serializer.serialize_str(&proxmox_base64::encode(self))
+ }
+
+ fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ finish_deserializing::<'de, N, D>(String::deserialize(deserializer)?)
+ }
+ }
+
+ impl<const N: usize> ByteArrayAsBase64<N> for Option<[u8; N]> {
+ fn ser<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
+ match self {
+ Some(s) => Self::ser(&Some(*s), serializer),
+ None => serializer.serialize_none(),
+ }
+ }
+
+ fn de<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
+ match Option::<String>::deserialize(deserializer)? {
+ Some(s) => Ok(Some(finish_deserializing::<'de, N, D>(s)?)),
+ None => Ok(None),
+ }
+ }
+ }
+
+ pub fn serialize<const N: usize, S, T>(data: &T, serializer: S) -> Result<S::Ok, S::Error>
+ where
+ S: Serializer,
+ T: ByteArrayAsBase64<N>,
+ {
+ <T as ByteArrayAsBase64<N>>::ser(data, serializer)
+ }
+
+ pub fn deserialize<'de, const N: usize, D, T>(deserializer: D) -> Result<T, D::Error>
+ where
+ D: Deserializer<'de>,
+ T: ByteArrayAsBase64<N>,
+ {
+ <T as ByteArrayAsBase64<N>>::de::<'de, D>(deserializer)
+ }
+}
--
2.52.0
_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel
next prev parent reply other threads:[~2026-01-16 15:33 UTC|newest]
Thread overview: 12+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-01-16 15:33 [pve-devel] [PATCH proxmox{, -ve-rs} 00/11] sdn: add wireguard fabric configuration support Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 01/11] serde: implement ini serializer Christoph Heiss
2026-01-16 15:33 ` Christoph Heiss [this message]
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 03/11] network-types: add ServiceEndpoint type as host/port tuple abstraction Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 04/11] schema: provide integer schema for node ports Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 05/11] schema: api-types: add ed25519 base64 encoded key schema Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 06/11] wireguard: init configuration support crate Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 07/11] wireguard: implement api for PublicKey Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox 08/11] wireguard: make per-peer preshared key optional Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 09/11] sdn-types: add wireguard-specific PersistentKeepalive api type Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 10/11] ve-config: fabric: refactor fabric config entry impl using macro Christoph Heiss
2026-01-16 15:33 ` [pve-devel] [PATCH proxmox-ve-rs 11/11] ve-config: sdn: fabrics: add wireguard section config types Christoph Heiss
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=20260116153317.1146323-3-c.heiss@proxmox.com \
--to=c.heiss@proxmox.com \
--cc=pve-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