This derives the Updater trait for FirewallRule, which generates a FirewallRuleUpdater struct for use in update APIs. The updater makes all fields optional, allowing partial updates.
Added test_updater_type() to verify that all updater fields have the correct types and can be properly initialized. Signed-off-by: Dietmar Maurer <[email protected]> --- proxmox-firewall-api-types/src/address.rs | 6 +++- proxmox-firewall-api-types/src/icmp_type.rs | 6 +++- proxmox-firewall-api-types/src/port.rs | 4 +++ proxmox-firewall-api-types/src/rule.rs | 39 +++++++++++++++++++-- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/proxmox-firewall-api-types/src/address.rs b/proxmox-firewall-api-types/src/address.rs index 46166352..196222ba 100644 --- a/proxmox-firewall-api-types/src/address.rs +++ b/proxmox-firewall-api-types/src/address.rs @@ -5,7 +5,7 @@ use super::{FirewallAliasReference, FirewallIpsetReference}; use anyhow::{bail, Error}; use proxmox_network_types::ip_address::{Cidr, Family, IpRange}; -use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema}; +use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema, UpdaterType}; /// A match for source or destination address. #[derive(Clone, Debug, PartialEq)] @@ -32,6 +32,10 @@ impl ApiType for FirewallAddressMatch { .schema(); } +impl UpdaterType for FirewallAddressMatch { + type Updater = Option<FirewallAddressMatch>; +} + fn verify_firewall_address_match(s: &str) -> Result<(), Error> { FirewallAddressMatch::from_str(s).map(|_| ()) } diff --git a/proxmox-firewall-api-types/src/icmp_type.rs b/proxmox-firewall-api-types/src/icmp_type.rs index b45c1505..46ddb58a 100644 --- a/proxmox-firewall-api-types/src/icmp_type.rs +++ b/proxmox-firewall-api-types/src/icmp_type.rs @@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize}; #[cfg(feature = "enum-fallback")] use proxmox_fixed_string::FixedString; -use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema}; +use proxmox_schema::{ApiStringFormat, ApiType, Schema, StringSchema, UpdaterType}; #[derive(Debug, Copy, Clone, PartialEq)] /// ICMP type, either named or numeric. @@ -26,6 +26,10 @@ impl ApiType for FirewallIcmpType { .schema(); } +impl UpdaterType for FirewallIcmpType { + type Updater = Option<FirewallIcmpType>; +} + fn verify_firewall_icmp_type(value: &str) -> Result<(), Error> { value.parse::<FirewallIcmpType>().map(|_| ()) } diff --git a/proxmox-firewall-api-types/src/port.rs b/proxmox-firewall-api-types/src/port.rs index 46989ba4..572cb439 100644 --- a/proxmox-firewall-api-types/src/port.rs +++ b/proxmox-firewall-api-types/src/port.rs @@ -84,6 +84,10 @@ pub const FIREWALL_DPORT_API_SCHEMA: Schema = StringSchema::new(concatcp!( .format(&ApiStringFormat::VerifyFn(verify_firewall_port_list)) .schema(); +impl UpdaterType for FirewallPortList { + type Updater = Option<FirewallPortList>; +} + serde_plain::derive_deserialize_from_fromstr!(FirewallPortList, "valid port list"); serde_plain::derive_serialize_from_display!(FirewallPortList); diff --git a/proxmox-firewall-api-types/src/rule.rs b/proxmox-firewall-api-types/src/rule.rs index 8588ca46..48869b25 100644 --- a/proxmox-firewall-api-types/src/rule.rs +++ b/proxmox-firewall-api-types/src/rule.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use proxmox_fixed_string::FixedString; use proxmox_schema::api_types::COMMENT_SCHEMA; -use proxmox_schema::{api, const_regex, ApiStringFormat}; +use proxmox_schema::{api, const_regex, ApiStringFormat, Updater}; use crate::{FirewallAddressMatch, FirewallIcmpType, FirewallPortList}; use crate::{FIREWALL_DPORT_API_SCHEMA, FIREWALL_SPORT_API_SCHEMA}; @@ -84,9 +84,10 @@ const_regex! { }, )] /// Firewall Rule. -#[derive(Debug, serde::Deserialize, serde::Serialize)] +#[derive(Debug, serde::Deserialize, serde::Serialize, Updater)] pub struct FirewallRule { /// Rule action ('ACCEPT', 'DROP', 'REJECT') or security group name. + #[updater(serde(default, skip_serializing_if = "Option::is_none"))] pub action: String, /// Descriptive comment. @@ -99,6 +100,7 @@ pub struct FirewallRule { /// Prevent changes if current configuration file has a different digest. /// This can be used to prevent concurrent modifications. #[serde(default, skip_serializing_if = "Option::is_none")] + #[updater(type = "Option<ConfigDigest>")] pub digest: Option<ConfigDigest>, /// Restrict TCP/UDP destination port. @@ -148,6 +150,7 @@ pub struct FirewallRule { pub sport: Option<FirewallPortList>, #[serde(rename = "type")] + #[updater(serde(default, skip_serializing_if = "Option::is_none"))] pub ty: FirewallRuleType, } @@ -179,6 +182,7 @@ serde_plain::derive_fromstr_from_deserialize!(FirewallRuleType); #[cfg(test)] mod test { use super::*; + use crate::FirewallIcmpTypeName; #[test] fn test_regex_compilation_firewall_rule_iface_re() { @@ -190,4 +194,35 @@ mod test { use regex::Regex; let _: &Regex = &FIREWALL_SECURITY_GROUP_RE; } + + #[test] + fn test_updater_type() { + // Test that we have all properties with correct types + let mut updater = FirewallRuleUpdater::default(); + + // Basic String fields - these are Option<String> in updater + updater.action = Some("ACCEPT".to_string()); + updater.comment = Some("test comment".to_string()); + updater.iface = Some("net0".to_string()); + updater.r#macro = Some("test-macro".to_string()); + updater.proto = Some("tcp".to_string()); + + // Numeric fields - these are Option<u64> in updater + updater.enable = Some(1); + updater.pos = Some(0); + + // Enum fields - these are Option<EnumType> in updater + updater.ty = Some(FirewallRuleType::In); + updater.log = Some(FirewallLogLevel::Info); + + // Fields with custom updater types #[updater(type = "Option<T>")] + // These are just Option<T> in the updater (not Option<Option<T>>) + // The #[updater(type)] attribute explicitly sets the updater field type + updater.dest = Some("192.168.1.1".parse().unwrap()); + updater.source = Some("10.0.0.1".parse().unwrap()); + updater.dport = Some("80".parse().unwrap()); + updater.sport = Some("1024:65535".parse().unwrap()); + updater.icmp_type = Some(FirewallIcmpType::Named(FirewallIcmpTypeName::EchoRequest)); + updater.digest = Some(ConfigDigest::from([0u8; 32])); + } } -- 2.47.3
