Skip to content

Commit 7c7c6a9

Browse files
committed
link: add Display + FromStr impls for bridge enums
Add Display and FromStr implementations for bridge-related enums to eliminate manual parsing/formatting in downstream crates: BridgeStpState Display: "no"/"kernel_stp"/"user_stp" FromStr: accepts name and numeric ("0"/"1"/"2") BridgeMulticastRouterType Display: "disabled"/"auto"/"permanent"/"temp" FromStr: accepts name and numeric BridgePortState Display: "disabled"/"listening"/"learning"/ "forwarding"/"blocking" FromStr: accepts string names BridgeFlag Display: "master"/"self" FromStr: accepts string names BridgeMode Display: "veb"/"vepa" FromStr: accepts string names VlanProtocol FromStr: case-insensitive "802.1q"/"802.1ad" (Display already existed) Other(d) variants produce raw numeric values matching iproute2 output. Signed-off-by: Gris Ge <cnfourt@gmail.com>
1 parent b97280b commit 7c7c6a9

5 files changed

Lines changed: 291 additions & 0 deletions

File tree

src/link/af_spec/bridge.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,30 @@ impl From<BridgeFlag> for u16 {
222222
}
223223
}
224224

225+
impl std::fmt::Display for BridgeFlag {
226+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
227+
match self {
228+
Self::Controller => f.write_str("master"),
229+
Self::LowerDev => f.write_str("self"),
230+
Self::Other(d) => write!(f, "{d}"),
231+
}
232+
}
233+
}
234+
235+
impl std::str::FromStr for BridgeFlag {
236+
type Err = DecodeError;
237+
238+
fn from_str(s: &str) -> Result<Self, Self::Err> {
239+
match s {
240+
"master" => Ok(Self::Controller),
241+
"self" => Ok(Self::LowerDev),
242+
_ => s.parse::<u16>().map(Self::from).map_err(|_| {
243+
DecodeError::from(format!("unknown bridge flag: {s}"))
244+
}),
245+
}
246+
}
247+
}
248+
225249
impl BridgeFlag {
226250
pub const LENGTH: usize = 2;
227251
}
@@ -259,6 +283,30 @@ impl From<BridgeMode> for u16 {
259283
}
260284
}
261285

286+
impl std::fmt::Display for BridgeMode {
287+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
288+
match self {
289+
Self::Veb => f.write_str("veb"),
290+
Self::Vepa => f.write_str("vepa"),
291+
Self::Other(d) => write!(f, "{d}"),
292+
}
293+
}
294+
}
295+
296+
impl std::str::FromStr for BridgeMode {
297+
type Err = DecodeError;
298+
299+
fn from_str(s: &str) -> Result<Self, Self::Err> {
300+
match s {
301+
"veb" => Ok(Self::Veb),
302+
"vepa" => Ok(Self::Vepa),
303+
_ => s.parse::<u16>().map(Self::from).map_err(|_| {
304+
DecodeError::from(format!("unknown bridge mode: {s}"))
305+
}),
306+
}
307+
}
308+
}
309+
262310
impl BridgeMode {
263311
pub const LENGTH: usize = 2;
264312
}

src/link/link_info/bridge.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,32 @@ impl From<BridgeStpState> for u32 {
743743
}
744744
}
745745

746+
impl std::fmt::Display for BridgeStpState {
747+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
748+
match self {
749+
Self::Disabled => f.write_str("no"),
750+
Self::KernelStp => f.write_str("kernel_stp"),
751+
Self::UserStp => f.write_str("user_stp"),
752+
Self::Other(d) => write!(f, "{d}"),
753+
}
754+
}
755+
}
756+
757+
impl std::str::FromStr for BridgeStpState {
758+
type Err = DecodeError;
759+
760+
fn from_str(s: &str) -> Result<Self, Self::Err> {
761+
match s {
762+
"no" => Ok(Self::Disabled),
763+
"kernel_stp" => Ok(Self::KernelStp),
764+
"user_stp" => Ok(Self::UserStp),
765+
_ => s.parse::<u32>().map(Self::from).map_err(|_| {
766+
DecodeError::from(format!("unknown bridge STP state: {s}"))
767+
}),
768+
}
769+
}
770+
}
771+
746772
const MDB_RTR_TYPE_DISABLED: u8 = 0;
747773
const MDB_RTR_TYPE_TEMP_QUERY: u8 = 1;
748774
const MDB_RTR_TYPE_PERM: u8 = 2;
@@ -787,3 +813,33 @@ impl From<BridgeMulticastRouterType> for u8 {
787813
}
788814
}
789815
}
816+
817+
impl std::fmt::Display for BridgeMulticastRouterType {
818+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
819+
match self {
820+
Self::Disabled => f.write_str("disabled"),
821+
Self::TempQuery => f.write_str("auto"),
822+
Self::Permanent => f.write_str("permanent"),
823+
Self::Temp => f.write_str("temp"),
824+
Self::Other(d) => write!(f, "{d}"),
825+
}
826+
}
827+
}
828+
829+
impl std::str::FromStr for BridgeMulticastRouterType {
830+
type Err = DecodeError;
831+
832+
fn from_str(s: &str) -> Result<Self, Self::Err> {
833+
match s {
834+
"disabled" => Ok(Self::Disabled),
835+
"auto" | "temp_query" => Ok(Self::TempQuery),
836+
"permanent" => Ok(Self::Permanent),
837+
"temp" => Ok(Self::Temp),
838+
_ => s.parse::<u8>().map(Self::from).map_err(|_| {
839+
DecodeError::from(format!(
840+
"unknown bridge multicast router type: {s}"
841+
))
842+
}),
843+
}
844+
}
845+
}

src/link/link_info/bridge_port.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,33 @@ impl From<BridgePortState> for u8 {
564564
}
565565
}
566566
}
567+
568+
impl std::fmt::Display for BridgePortState {
569+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
570+
match self {
571+
Self::Disabled => f.write_str("disabled"),
572+
Self::Listening => f.write_str("listening"),
573+
Self::Learning => f.write_str("learning"),
574+
Self::Forwarding => f.write_str("forwarding"),
575+
Self::Blocking => f.write_str("blocking"),
576+
Self::Other(d) => write!(f, "{d}"),
577+
}
578+
}
579+
}
580+
581+
impl std::str::FromStr for BridgePortState {
582+
type Err = DecodeError;
583+
584+
fn from_str(s: &str) -> Result<Self, Self::Err> {
585+
match s {
586+
"disabled" => Ok(Self::Disabled),
587+
"listening" => Ok(Self::Listening),
588+
"learning" => Ok(Self::Learning),
589+
"forwarding" => Ok(Self::Forwarding),
590+
"blocking" => Ok(Self::Blocking),
591+
_ => s.parse::<u8>().map(Self::from).map_err(|_| {
592+
DecodeError::from(format!("unknown bridge port state: {s}"))
593+
}),
594+
}
595+
}
596+
}

src/link/tests/bridge.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,3 +1231,144 @@ fn test_bridge_netns_immutable() {
12311231

12321232
expected.emit(&mut buf);
12331233
}
1234+
1235+
#[test]
1236+
fn test_bridge_flag_display() {
1237+
assert_eq!("master", BridgeFlag::Controller.to_string());
1238+
assert_eq!("self", BridgeFlag::LowerDev.to_string());
1239+
assert_eq!("255", BridgeFlag::Other(255).to_string());
1240+
}
1241+
1242+
#[test]
1243+
fn test_bridge_flag_from_str() {
1244+
use std::str::FromStr;
1245+
assert_eq!(BridgeFlag::Controller, "master".parse().unwrap());
1246+
assert_eq!(BridgeFlag::LowerDev, "self".parse().unwrap());
1247+
assert_eq!(BridgeFlag::from(1), "1".parse().unwrap());
1248+
assert_eq!(BridgeFlag::from(2), "2".parse().unwrap());
1249+
assert_eq!(BridgeFlag::Other(99), "99".parse().unwrap());
1250+
assert!(BridgeFlag::from_str("bogus").is_err());
1251+
}
1252+
1253+
#[test]
1254+
fn test_bridge_mode_display() {
1255+
assert_eq!("veb", BridgeMode::Veb.to_string());
1256+
assert_eq!("vepa", BridgeMode::Vepa.to_string());
1257+
assert_eq!("255", BridgeMode::Other(255).to_string());
1258+
}
1259+
1260+
#[test]
1261+
fn test_bridge_mode_from_str() {
1262+
use std::str::FromStr;
1263+
assert_eq!(BridgeMode::Veb, "veb".parse().unwrap());
1264+
assert_eq!(BridgeMode::Vepa, "vepa".parse().unwrap());
1265+
assert_eq!(BridgeMode::from(0), "0".parse().unwrap());
1266+
assert_eq!(BridgeMode::from(1), "1".parse().unwrap());
1267+
assert_eq!(BridgeMode::Other(99), "99".parse().unwrap());
1268+
assert!(BridgeMode::from_str("bogus").is_err());
1269+
}
1270+
1271+
#[test]
1272+
fn test_bridge_stp_state_display() {
1273+
assert_eq!("no", BridgeStpState::Disabled.to_string());
1274+
assert_eq!("kernel_stp", BridgeStpState::KernelStp.to_string());
1275+
assert_eq!("user_stp", BridgeStpState::UserStp.to_string());
1276+
assert_eq!("255", BridgeStpState::Other(255).to_string());
1277+
}
1278+
1279+
#[test]
1280+
fn test_bridge_stp_state_from_str() {
1281+
use std::str::FromStr;
1282+
assert_eq!(BridgeStpState::Disabled, "no".parse().unwrap());
1283+
assert_eq!(BridgeStpState::KernelStp, "kernel_stp".parse().unwrap());
1284+
assert_eq!(BridgeStpState::UserStp, "user_stp".parse().unwrap());
1285+
assert_eq!(BridgeStpState::Disabled, "0".parse().unwrap());
1286+
assert_eq!(BridgeStpState::KernelStp, "1".parse().unwrap());
1287+
assert_eq!(BridgeStpState::UserStp, "2".parse().unwrap());
1288+
assert_eq!(BridgeStpState::Other(99), "99".parse().unwrap());
1289+
assert!(BridgeStpState::from_str("bogus").is_err());
1290+
}
1291+
1292+
#[test]
1293+
fn test_bridge_multicast_router_type_display() {
1294+
assert_eq!("disabled", BridgeMulticastRouterType::Disabled.to_string());
1295+
assert_eq!("auto", BridgeMulticastRouterType::TempQuery.to_string());
1296+
assert_eq!(
1297+
"permanent",
1298+
BridgeMulticastRouterType::Permanent.to_string()
1299+
);
1300+
assert_eq!("temp", BridgeMulticastRouterType::Temp.to_string());
1301+
assert_eq!("255", BridgeMulticastRouterType::Other(255).to_string());
1302+
}
1303+
1304+
#[test]
1305+
fn test_bridge_multicast_router_type_from_str() {
1306+
use std::str::FromStr;
1307+
assert_eq!(
1308+
BridgeMulticastRouterType::Disabled,
1309+
"disabled".parse().unwrap()
1310+
);
1311+
assert_eq!(
1312+
BridgeMulticastRouterType::TempQuery,
1313+
"auto".parse().unwrap()
1314+
);
1315+
assert_eq!(
1316+
BridgeMulticastRouterType::TempQuery,
1317+
"temp_query".parse().unwrap()
1318+
);
1319+
assert_eq!(
1320+
BridgeMulticastRouterType::Permanent,
1321+
"permanent".parse().unwrap()
1322+
);
1323+
assert_eq!(BridgeMulticastRouterType::Temp, "temp".parse().unwrap());
1324+
assert_eq!(BridgeMulticastRouterType::from(0), "0".parse().unwrap());
1325+
assert_eq!(BridgeMulticastRouterType::from(1), "1".parse().unwrap());
1326+
assert_eq!(BridgeMulticastRouterType::from(2), "2".parse().unwrap());
1327+
assert_eq!(BridgeMulticastRouterType::from(3), "3".parse().unwrap());
1328+
assert_eq!(BridgeMulticastRouterType::Other(99), "99".parse().unwrap());
1329+
assert!(BridgeMulticastRouterType::from_str("bogus").is_err());
1330+
}
1331+
1332+
#[test]
1333+
fn test_bridge_port_state_display() {
1334+
assert_eq!("disabled", BridgePortState::Disabled.to_string());
1335+
assert_eq!("listening", BridgePortState::Listening.to_string());
1336+
assert_eq!("learning", BridgePortState::Learning.to_string());
1337+
assert_eq!("forwarding", BridgePortState::Forwarding.to_string());
1338+
assert_eq!("blocking", BridgePortState::Blocking.to_string());
1339+
assert_eq!("255", BridgePortState::Other(255).to_string());
1340+
}
1341+
1342+
#[test]
1343+
fn test_bridge_port_state_from_str() {
1344+
use std::str::FromStr;
1345+
assert_eq!(BridgePortState::Disabled, "disabled".parse().unwrap());
1346+
assert_eq!(BridgePortState::Listening, "listening".parse().unwrap());
1347+
assert_eq!(BridgePortState::Learning, "learning".parse().unwrap());
1348+
assert_eq!(BridgePortState::Forwarding, "forwarding".parse().unwrap());
1349+
assert_eq!(BridgePortState::Blocking, "blocking".parse().unwrap());
1350+
assert_eq!(BridgePortState::from(0), "0".parse().unwrap());
1351+
assert_eq!(BridgePortState::from(1), "1".parse().unwrap());
1352+
assert_eq!(BridgePortState::from(2), "2".parse().unwrap());
1353+
assert_eq!(BridgePortState::from(3), "3".parse().unwrap());
1354+
assert_eq!(BridgePortState::from(4), "4".parse().unwrap());
1355+
assert_eq!(BridgePortState::Other(99), "99".parse().unwrap());
1356+
assert!(BridgePortState::from_str("bogus").is_err());
1357+
}
1358+
1359+
#[test]
1360+
fn test_vlan_protocol_display() {
1361+
assert_eq!("802.1q", VlanProtocol::Ieee8021Q.to_string());
1362+
assert_eq!("802.1ad", VlanProtocol::Ieee8021Ad.to_string());
1363+
}
1364+
1365+
#[test]
1366+
fn test_vlan_protocol_from_str() {
1367+
use std::str::FromStr;
1368+
assert_eq!(VlanProtocol::Ieee8021Q, "802.1q".parse().unwrap());
1369+
assert_eq!(VlanProtocol::Ieee8021Ad, "802.1ad".parse().unwrap());
1370+
assert_eq!(VlanProtocol::Ieee8021Q, "802.1Q".parse().unwrap());
1371+
assert_eq!(VlanProtocol::Ieee8021Ad, "802.1AD".parse().unwrap());
1372+
assert!(VlanProtocol::from_str("bogus").is_err());
1373+
assert!(VlanProtocol::from_str("802.1Qq").is_err());
1374+
}

src/link/vlan_protocol.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// SPDX-License-Identifier: MIT
22

3+
use netlink_packet_core::DecodeError;
4+
35
const ETH_P_8021Q: u16 = 0x8100;
46
const ETH_P_8021AD: u16 = 0x88A8;
57

@@ -51,3 +53,17 @@ impl std::fmt::Display for VlanProtocol {
5153
)
5254
}
5355
}
56+
57+
impl std::str::FromStr for VlanProtocol {
58+
type Err = DecodeError;
59+
60+
fn from_str(s: &str) -> Result<Self, Self::Err> {
61+
if s.eq_ignore_ascii_case("802.1q") {
62+
Ok(Self::Ieee8021Q)
63+
} else if s.eq_ignore_ascii_case("802.1ad") {
64+
Ok(Self::Ieee8021Ad)
65+
} else {
66+
Err(DecodeError::from(format!("unknown VLAN protocol: {s}")))
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)