From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [45.144.208.40]) by lore.proxmox.com (Postfix) with ESMTPS id 372731FF09B for ; Mon, 14 Sep 2026 14:32:53 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 1F502215D2; Mon, 14 Sep 2026 14:32:50 +0200 (CEST) From: Dominik Csapak To: pdm-devel@lists.proxmox.com Subject: [PATCH proxmox v3 1/2] add proxmox-const-utils crate, factor out checks from proxmox-schema Date: Mon, 14 Sep 2026 14:30:44 +0200 Message-ID: <20260914123243.3216993-2-d.csapak@proxmox.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: <20260914123243.3216993-1-d.csapak@proxmox.com> References: <20260914123243.3216993-1-d.csapak@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.465 Adjusted score from AWL reputation of From: address DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment (newer systems) KAM_SHORT 0.001 Use of a URL Shortener for very short URL RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: QSRA7DKUST6WHCSNRKKEAS4NC5ZWGKJP X-Message-ID-Hash: QSRA7DKUST6WHCSNRKKEAS4NC5ZWGKJP X-MailFrom: d.csapak@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Datacenter Manager development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: We want to do some const fn checks in multiple crates, and proxmox-schema already contained the one we need, so factor it out. For convenience also add a simple `byte_string_eq`. Also add some basic tests. Signed-off-by: Dominik Csapak --- Cargo.toml | 2 + proxmox-const-utils/Cargo.toml | 13 +++++ proxmox-const-utils/debian/changelog | 5 ++ proxmox-const-utils/debian/control | 30 +++++++++++ proxmox-const-utils/debian/copyright | 18 +++++++ proxmox-const-utils/debian/debcargo.toml | 7 +++ proxmox-const-utils/src/lib.rs | 66 ++++++++++++++++++++++++ proxmox-schema/Cargo.toml | 1 + proxmox-schema/src/const_test_utils.rs | 25 --------- proxmox-schema/src/lib.rs | 2 - proxmox-schema/src/schema.rs | 10 ++-- 11 files changed, 147 insertions(+), 32 deletions(-) create mode 100644 proxmox-const-utils/Cargo.toml create mode 100644 proxmox-const-utils/debian/changelog create mode 100644 proxmox-const-utils/debian/control create mode 100644 proxmox-const-utils/debian/copyright create mode 100644 proxmox-const-utils/debian/debcargo.toml create mode 100644 proxmox-const-utils/src/lib.rs delete mode 100644 proxmox-schema/src/const_test_utils.rs diff --git a/Cargo.toml b/Cargo.toml index f631b404..ba29c8bb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "proxmox-client", "proxmox-compression", "proxmox-config-digest", + "proxmox-const-utils", "proxmox-daemon", "proxmox-deb-version", "proxmox-disks", @@ -166,6 +167,7 @@ proxmox-auth-api = { version = "1.0.5", path = "proxmox-auth-api" } proxmox-async = { version = "0.5.0", path = "proxmox-async" } proxmox-base64 = { version = "1.0.0", path = "proxmox-base64" } proxmox-compression = { version = "1.0.0", path = "proxmox-compression" } +proxmox-const-utils = { version = "0.1.0", path = "proxmox-const-utils" } proxmox-daemon = { version = "1.0.0", path = "proxmox-daemon" } proxmox-disks = { version = "0.2.0", path = "proxmox-disks" } proxmox-fixed-string = { version = "0.1.0", path = "proxmox-fixed-string" } diff --git a/proxmox-const-utils/Cargo.toml b/proxmox-const-utils/Cargo.toml new file mode 100644 index 00000000..914c5fd3 --- /dev/null +++ b/proxmox-const-utils/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "proxmox-const-utils" +description = "const fn utilities used by proxmox crates" +version = "0.1.0" +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +homepage.workspace = true +exclude.workspace = true +rust-version.workspace = true + +[dependencies] diff --git a/proxmox-const-utils/debian/changelog b/proxmox-const-utils/debian/changelog new file mode 100644 index 00000000..fc49a60f --- /dev/null +++ b/proxmox-const-utils/debian/changelog @@ -0,0 +1,5 @@ +rust-proxmox-const-utils (0.1.0) trixie; urgency=medium + + * initial version + + -- Proxmox Support Team Mon, 14 Sep 2026 13:38:43 +0200 diff --git a/proxmox-const-utils/debian/control b/proxmox-const-utils/debian/control new file mode 100644 index 00000000..e45bf06a --- /dev/null +++ b/proxmox-const-utils/debian/control @@ -0,0 +1,30 @@ +Source: rust-proxmox-const-utils +Section: rust +Priority: optional +Build-Depends: debhelper-compat (= 13), + dh-sequence-cargo +Build-Depends-Arch: cargo:native , + rustc:native (>= 1.85) , + libstd-rust-dev +Maintainer: Proxmox Support Team +Standards-Version: 4.7.2 +Vcs-Git: git://git.proxmox.com/git/proxmox.git +Vcs-Browser: https://git.proxmox.com/?p=proxmox.git +Homepage: https://proxmox.com +X-Cargo-Crate: proxmox-const-utils + +Package: librust-proxmox-const-utils-dev +Architecture: any +Multi-Arch: same +Depends: + ${misc:Depends} +Provides: + librust-proxmox-const-utils+default-dev (= ${binary:Version}), + librust-proxmox-const-utils-0-dev (= ${binary:Version}), + librust-proxmox-const-utils-0+default-dev (= ${binary:Version}), + librust-proxmox-const-utils-0.1-dev (= ${binary:Version}), + librust-proxmox-const-utils-0.1+default-dev (= ${binary:Version}), + librust-proxmox-const-utils-0.1.0-dev (= ${binary:Version}), + librust-proxmox-const-utils-0.1.0+default-dev (= ${binary:Version}) +Description: Const fn utilities used by proxmox crates - Rust source code + Source code for Debianized Rust crate "proxmox-const-utils" diff --git a/proxmox-const-utils/debian/copyright b/proxmox-const-utils/debian/copyright new file mode 100644 index 00000000..cb3e5adc --- /dev/null +++ b/proxmox-const-utils/debian/copyright @@ -0,0 +1,18 @@ +Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ + +Files: + * +Copyright: 2025 - 2026 Proxmox Server Solutions GmbH +License: AGPL-3.0-or-later + This program is free software: you can redistribute it and/or modify it under + the terms of the GNU Affero General Public License as published by the Free + Software Foundation, either version 3 of the License, or (at your option) any + later version. + . + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more + details. + . + You should have received a copy of the GNU Affero General Public License along + with this program. If not, see . diff --git a/proxmox-const-utils/debian/debcargo.toml b/proxmox-const-utils/debian/debcargo.toml new file mode 100644 index 00000000..b7864cdb --- /dev/null +++ b/proxmox-const-utils/debian/debcargo.toml @@ -0,0 +1,7 @@ +overlay = "." +crate_src_path = ".." +maintainer = "Proxmox Support Team " + +[source] +vcs_git = "git://git.proxmox.com/git/proxmox.git" +vcs_browser = "https://git.proxmox.com/?p=proxmox.git" diff --git a/proxmox-const-utils/src/lib.rs b/proxmox-const-utils/src/lib.rs new file mode 100644 index 00000000..a4a4ceff --- /dev/null +++ b/proxmox-const-utils/src/lib.rs @@ -0,0 +1,66 @@ +/// Note: this only compares *bytes* and is not strictly speaking equivalent to str::cmp! +pub const fn byte_string_cmp(a: &[u8], b: &[u8]) -> std::cmp::Ordering { + use std::cmp::Ordering::*; + + // const-version of `min(a.len(), b.len())` while simultaneously remembering + // `cmp(a.len(), b.len())`. + let (end, len_result) = if a.len() < b.len() { + (a.len(), Less) + } else if a.len() > b.len() { + (b.len(), Greater) + } else { + (a.len(), Equal) + }; + + let mut i = 0; + while i != end { + if a[i] < b[i] { + return Less; + } else if a[i] > b[i] { + return Greater; + } + i += 1; + } + len_result +} + +/// As with [`byte_string_cmp`] this only compares bytes. +pub const fn byte_string_eq(a: &[u8], b: &[u8]) -> bool { + matches!(byte_string_cmp(a, b), std::cmp::Ordering::Equal) +} + +#[cfg(test)] +mod test { + use crate::{byte_string_cmp, byte_string_eq}; + use std::cmp::Ordering::{self, *}; + + fn str_cmp(a: &str, b: &str, res: Ordering) { + assert_eq!(byte_string_cmp(a.as_bytes(), b.as_bytes()), res); + } + + fn str_eq(a: &str, b: &str, res: bool) { + assert_eq!(byte_string_eq(a.as_bytes(), b.as_bytes()), res); + } + + #[test] + fn test_cmp() { + str_cmp("foo", "bar", Greater); + str_cmp("bar", "foo", Less); + str_cmp("foo", "fooo", Less); + str_cmp("fooo", "foo", Greater); + str_cmp("foo", "foo", Equal); + str_cmp("", "", Equal); + str_cmp("", "foo", Less); + str_cmp("foo", "", Greater); + } + + #[test] + fn test_eq() { + str_eq("", "", true); + str_eq("foo", "foo", true); + str_eq("foo", "", false); + str_eq("", "foo", false); + str_eq("bar", "foo", false); + str_eq("foo", "bar", false); + } +} diff --git a/proxmox-schema/Cargo.toml b/proxmox-schema/Cargo.toml index 79eab333..b2de56dd 100644 --- a/proxmox-schema/Cargo.toml +++ b/proxmox-schema/Cargo.toml @@ -24,6 +24,7 @@ libc = { workspace = true, optional = true } nix = { workspace = true, optional = true, features = [ "feature" ] } proxmox-api-macro = { workspace = true, optional = true } +proxmox-const-utils.workspace = true [dev-dependencies] url.workspace = true diff --git a/proxmox-schema/src/const_test_utils.rs b/proxmox-schema/src/const_test_utils.rs deleted file mode 100644 index 3613ac35..00000000 --- a/proxmox-schema/src/const_test_utils.rs +++ /dev/null @@ -1,25 +0,0 @@ -/// Note: this only compares *bytes* and is not strictly speaking equivalent to str::cmp! -pub const fn byte_string_cmp(a: &[u8], b: &[u8]) -> std::cmp::Ordering { - use std::cmp::Ordering::*; - - // const-version of `min(a.len(), b.len())` while simultaneously remembering - // `cmp(a.len(), b.len())`. - let (end, len_result) = if a.len() < b.len() { - (a.len(), Less) - } else if a.len() > b.len() { - (b.len(), Greater) - } else { - (a.len(), Equal) - }; - - let mut i = 0; - while i != end { - if a[i] < b[i] { - return Less; - } else if a[i] > b[i] { - return Greater; - } - i += 1; - } - len_result -} diff --git a/proxmox-schema/src/lib.rs b/proxmox-schema/src/lib.rs index 727822f0..5175280c 100644 --- a/proxmox-schema/src/lib.rs +++ b/proxmox-schema/src/lib.rs @@ -36,5 +36,3 @@ pub mod upid; #[cfg(feature = "api-types")] pub mod api_types; - -pub(crate) mod const_test_utils; diff --git a/proxmox-schema/src/schema.rs b/proxmox-schema/src/schema.rs index 378c6290..97e502a7 100644 --- a/proxmox-schema/src/schema.rs +++ b/proxmox-schema/src/schema.rs @@ -655,7 +655,7 @@ const fn assert_properties_sorted(properties: SchemaPropertyMap) { while i != properties.len() { let cur = properties[i].0; if let Some(prev) = prev { - match crate::const_test_utils::byte_string_cmp(prev.as_bytes(), cur.as_bytes()) { + match proxmox_const_utils::byte_string_cmp(prev.as_bytes(), cur.as_bytes()) { Ordering::Greater => panic!("object schema properties must be sorted"), Ordering::Equal => panic!("duplicate object schema properties not allowed"), Ordering::Less => (), @@ -672,7 +672,7 @@ const fn property_map_contains(properties: SchemaPropertyMap, needle: &'static s let mut i = 0; while i != properties.len() { if let Ordering::Equal = - crate::const_test_utils::byte_string_cmp(properties[i].0.as_bytes(), needle.as_bytes()) + proxmox_const_utils::byte_string_cmp(properties[i].0.as_bytes(), needle.as_bytes()) { return true; } @@ -688,7 +688,7 @@ const fn assert_key_not_alias(key: &str, aliases: PropertyAliasMap) { while i != aliases.len() { let (alias, _) = aliases[i]; if let Ordering::Equal = - crate::const_test_utils::byte_string_cmp(alias.as_bytes(), key.as_bytes()) + proxmox_const_utils::byte_string_cmp(alias.as_bytes(), key.as_bytes()) { panic!("key must reference a canonical property, not an alias"); } @@ -704,7 +704,7 @@ const fn assert_property_aliases_valid(aliases: PropertyAliasMap, properties: Sc while i != aliases.len() { let (alias, target) = aliases[i]; if let Some(prev) = prev { - match crate::const_test_utils::byte_string_cmp(prev.as_bytes(), alias.as_bytes()) { + match proxmox_const_utils::byte_string_cmp(prev.as_bytes(), alias.as_bytes()) { Ordering::Greater => panic!("property aliases must be sorted by alias name"), Ordering::Equal => panic!("duplicate property alias not allowed"), Ordering::Less => (), @@ -1050,7 +1050,7 @@ const fn assert_one_of_list_is_sorted(list: &[(&str, &Schema)]) { while i != list.len() { let cur = list[i].0; if let Some(prev) = prev { - match crate::const_test_utils::byte_string_cmp(prev.as_bytes(), cur.as_bytes()) { + match proxmox_const_utils::byte_string_cmp(prev.as_bytes(), cur.as_bytes()) { Ordering::Greater => panic!("oneOf variant list must be sorted"), Ordering::Equal => panic!("multiple variants of the same type"), Ordering::Less => (), -- 2.47.3