Skip to content

Commit

Permalink
Wrap macaddr::MacAddr6 in newtype MacAddress
Browse files Browse the repository at this point in the history
This allows us to reimplement the Serialize and
Deserialize traits to make use of the hex-style
format.
  • Loading branch information
milliams committed Oct 29, 2023
1 parent e2b9442 commit af8df5c
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 14 deletions.
6 changes: 3 additions & 3 deletions src/network/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ pub use self::networks::{Network, NetworkQuery, NewNetwork};
pub use self::ports::{NewPort, Port, PortIpAddress, PortIpRequest, PortQuery};
pub use self::protocol::{
AllocationPool, AllowedAddressPair, ConntrackHelper, ExternalGateway, FloatingIpSortKey,
FloatingIpStatus, Helper, HostRoute, IpVersion, Ipv6Mode, NetworkProtocol, NetworkSortKey,
NetworkStatus, PortExtraDhcpOption, PortForwarding, PortSortKey, RouterSortKey, RouterStatus,
SubnetSortKey,
FloatingIpStatus, Helper, HostRoute, IpVersion, Ipv6Mode, MacAddress, NetworkProtocol,
NetworkSortKey, NetworkStatus, PortExtraDhcpOption, PortForwarding, PortSortKey, RouterSortKey,
RouterStatus, SubnetSortKey,
};
pub use self::routers::{NewRouter, Router, RouterQuery};
pub use self::subnets::{NewSubnet, Subnet, SubnetQuery};
9 changes: 4 additions & 5 deletions src/network/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use std::time::Duration;
use async_trait::async_trait;
use chrono::{DateTime, FixedOffset};
use futures::stream::{Stream, TryStreamExt};
use macaddr::MacAddr6;

use super::super::common::{
NetworkRef, PortRef, Refresh, ResourceIterator, ResourceQuery, SecurityGroupRef, SubnetRef,
Expand All @@ -31,7 +30,7 @@ use super::super::session::Session;
use super::super::utils::Query;
use super::super::waiter::DeletionWaiter;
use super::super::{Result, Sort};
use super::{api, protocol, Network, Subnet};
use super::{api, protocol, MacAddress, Network, Subnet};

/// A query to port list.
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -211,12 +210,12 @@ impl Port {

transparent_property! {
#[doc = "MAC address of the port."]
mac_address: MacAddr6
mac_address: MacAddress
}

update_field! {
#[doc = "Update the MAC address (admin-only)."]
set_mac_address, with_mac_address -> mac_address: MacAddr6
set_mac_address, with_mac_address -> mac_address: MacAddress
}

transparent_property! {
Expand Down Expand Up @@ -573,7 +572,7 @@ impl NewPort {

creation_inner_field! {
#[doc = "Set MAC address for the port (generated otherwise)."]
set_mac_address, with_mac_address -> mac_address: MacAddr6
set_mac_address, with_mac_address -> mac_address: MacAddress
}

creation_inner_field! {
Expand Down
90 changes: 84 additions & 6 deletions src/network/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,8 @@ use std::net;
use std::ops::Not;

use chrono::{DateTime, FixedOffset};
use macaddr::MacAddr6;
use osauth::common::empty_as_default;
use serde::{Deserialize, Serialize};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;

use super::super::common::{NetworkRef, SecurityGroupRef};
Expand Down Expand Up @@ -330,12 +329,56 @@ pub struct FixedIp {
pub subnet_id: String,
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Default, Ord, PartialOrd, Hash)]
pub struct MacAddress(macaddr::MacAddr6);

impl MacAddress {
pub fn is_nil(&self) -> bool {
self.0.is_nil()
}
}

impl std::ops::Deref for MacAddress {
type Target = macaddr::MacAddr6;

fn deref(&self) -> &Self::Target {
&self.0
}
}

impl std::str::FromStr for MacAddress {
type Err = macaddr::ParseError;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(Self(s.parse::<macaddr::MacAddr6>()?))
}
}

impl Serialize for MacAddress {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}

impl<'de> Deserialize<'de> for MacAddress {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
s.parse().map_err(serde::de::Error::custom)
}
}

/// A port's IP address.
#[derive(Debug, Clone, Deserialize, Serialize, Copy)]
pub struct AllowedAddressPair {
pub ip_address: net::IpAddr,
#[serde(skip_serializing_if = "Option::is_none")]
pub mac_address: Option<MacAddr6>,
pub mac_address: Option<MacAddress>,
}

/// A port.
Expand Down Expand Up @@ -382,8 +425,8 @@ pub struct Port {
pub fixed_ips: Vec<FixedIp>,
#[serde(skip_serializing)]
pub id: String,
#[serde(skip_serializing_if = "MacAddr6::is_nil")]
pub mac_address: MacAddr6,
#[serde(skip_serializing_if = "MacAddress::is_nil")]
pub mac_address: MacAddress,
#[serde(
deserialize_with = "empty_as_default",
skip_serializing_if = "Option::is_none"
Expand Down Expand Up @@ -420,7 +463,7 @@ pub struct PortUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
pub fixed_ips: Option<Vec<FixedIp>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mac_address: Option<MacAddr6>,
pub mac_address: Option<MacAddress>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
Expand Down Expand Up @@ -855,3 +898,38 @@ pub struct FloatingIpUpdateRoot {
pub struct FloatingIpsRoot {
pub floatingips: Vec<FloatingIp>,
}

#[cfg(test)]
mod test {
use super::*;

#[test]
fn test_parse_macaddr() {
// Test that a JSON deserialisation of MAC addresses work
let a: AllowedAddressPair = serde_json::from_value(
serde_json::json!({"ip_address":"0.0.0.0", "mac_address":"ab:aa:aa:aa:aa:aa"}),
)
.expect("Could not parse this JSON");
assert_eq!(
a.mac_address.expect("MAC address is missing").to_string(),
"AB:AA:AA:AA:AA:AA"
);

// Test that a JSON serialisation of MAC addresses work
assert_eq!(
serde_json::to_value(&a)
.expect("Could not serialize")
.get("mac_address")
.expect("No mac_address")
.as_str()
.expect("No string found"),
"AB:AA:AA:AA:AA:AA"
);

// Test that missing MAC addresses are parsed as None
let a: AllowedAddressPair =
serde_json::from_value(serde_json::json!({"ip_address":"0.0.0.0"}))
.expect("Cannot parse this JSON");
assert_eq!(a.mac_address, None);
}
}

0 comments on commit af8df5c

Please sign in to comment.