Skip to content

Commit 0032b0d

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 0032b0d

5 files changed

Lines changed: 303 additions & 0 deletions

File tree

src/link/af_spec/bridge.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,31 @@ 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
243+
.parse::<u16>()
244+
.map(Self::from)
245+
.map_err(|_| DecodeError::from(format!("unknown bridge flag: {s}"))),
246+
}
247+
}
248+
}
249+
225250
impl BridgeFlag {
226251
pub const LENGTH: usize = 2;
227252
}
@@ -259,6 +284,31 @@ impl From<BridgeMode> for u16 {
259284
}
260285
}
261286

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

src/link/link_info/bridge.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,33 @@ 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
766+
.parse::<u32>()
767+
.map(Self::from)
768+
.map_err(|_| DecodeError::from(format!("unknown bridge STP state: {s}"))),
769+
}
770+
}
771+
}
772+
746773
const MDB_RTR_TYPE_DISABLED: u8 = 0;
747774
const MDB_RTR_TYPE_TEMP_QUERY: u8 = 1;
748775
const MDB_RTR_TYPE_PERM: u8 = 2;
@@ -787,3 +814,36 @@ impl From<BridgeMulticastRouterType> for u8 {
787814
}
788815
}
789816
}
817+
818+
impl std::fmt::Display for BridgeMulticastRouterType {
819+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
820+
match self {
821+
Self::Disabled => f.write_str("disabled"),
822+
Self::TempQuery => f.write_str("auto"),
823+
Self::Permanent => f.write_str("permanent"),
824+
Self::Temp => f.write_str("temp"),
825+
Self::Other(d) => write!(f, "{d}"),
826+
}
827+
}
828+
}
829+
830+
impl std::str::FromStr for BridgeMulticastRouterType {
831+
type Err = DecodeError;
832+
833+
fn from_str(s: &str) -> Result<Self, Self::Err> {
834+
match s {
835+
"disabled" => Ok(Self::Disabled),
836+
"auto" | "temp_query" => Ok(Self::TempQuery),
837+
"permanent" => Ok(Self::Permanent),
838+
"temp" => Ok(Self::Temp),
839+
_ => s
840+
.parse::<u8>()
841+
.map(Self::from)
842+
.map_err(|_| {
843+
DecodeError::from(format!(
844+
"unknown bridge multicast router type: {s}"
845+
))
846+
}),
847+
}
848+
}
849+
}

src/link/link_info/bridge_port.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,3 +564,34 @@ 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
592+
.parse::<u8>()
593+
.map(Self::from)
594+
.map_err(|_| DecodeError::from(format!("unknown bridge port state: {s}"))),
595+
}
596+
}
597+
}

src/link/tests/bridge.rs

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,3 +1231,147 @@ 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!("permanent", BridgeMulticastRouterType::Permanent.to_string());
1297+
assert_eq!("temp", BridgeMulticastRouterType::Temp.to_string());
1298+
assert_eq!("255", BridgeMulticastRouterType::Other(255).to_string());
1299+
}
1300+
1301+
#[test]
1302+
fn test_bridge_multicast_router_type_from_str() {
1303+
use std::str::FromStr;
1304+
assert_eq!(
1305+
BridgeMulticastRouterType::Disabled,
1306+
"disabled".parse().unwrap()
1307+
);
1308+
assert_eq!(
1309+
BridgeMulticastRouterType::TempQuery,
1310+
"auto".parse().unwrap()
1311+
);
1312+
assert_eq!(
1313+
BridgeMulticastRouterType::TempQuery,
1314+
"temp_query".parse().unwrap()
1315+
);
1316+
assert_eq!(
1317+
BridgeMulticastRouterType::Permanent,
1318+
"permanent".parse().unwrap()
1319+
);
1320+
assert_eq!(
1321+
BridgeMulticastRouterType::Temp,
1322+
"temp".parse().unwrap()
1323+
);
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!(
1329+
BridgeMulticastRouterType::Other(99),
1330+
"99".parse().unwrap()
1331+
);
1332+
assert!(BridgeMulticastRouterType::from_str("bogus").is_err());
1333+
}
1334+
1335+
#[test]
1336+
fn test_bridge_port_state_display() {
1337+
assert_eq!("disabled", BridgePortState::Disabled.to_string());
1338+
assert_eq!("listening", BridgePortState::Listening.to_string());
1339+
assert_eq!("learning", BridgePortState::Learning.to_string());
1340+
assert_eq!("forwarding", BridgePortState::Forwarding.to_string());
1341+
assert_eq!("blocking", BridgePortState::Blocking.to_string());
1342+
assert_eq!("255", BridgePortState::Other(255).to_string());
1343+
}
1344+
1345+
#[test]
1346+
fn test_bridge_port_state_from_str() {
1347+
use std::str::FromStr;
1348+
assert_eq!(BridgePortState::Disabled, "disabled".parse().unwrap());
1349+
assert_eq!(BridgePortState::Listening, "listening".parse().unwrap());
1350+
assert_eq!(BridgePortState::Learning, "learning".parse().unwrap());
1351+
assert_eq!(BridgePortState::Forwarding, "forwarding".parse().unwrap());
1352+
assert_eq!(BridgePortState::Blocking, "blocking".parse().unwrap());
1353+
assert_eq!(BridgePortState::from(0), "0".parse().unwrap());
1354+
assert_eq!(BridgePortState::from(1), "1".parse().unwrap());
1355+
assert_eq!(BridgePortState::from(2), "2".parse().unwrap());
1356+
assert_eq!(BridgePortState::from(3), "3".parse().unwrap());
1357+
assert_eq!(BridgePortState::from(4), "4".parse().unwrap());
1358+
assert_eq!(BridgePortState::Other(99), "99".parse().unwrap());
1359+
assert!(BridgePortState::from_str("bogus").is_err());
1360+
}
1361+
1362+
#[test]
1363+
fn test_vlan_protocol_display() {
1364+
assert_eq!("802.1q", VlanProtocol::Ieee8021Q.to_string());
1365+
assert_eq!("802.1ad", VlanProtocol::Ieee8021Ad.to_string());
1366+
}
1367+
1368+
#[test]
1369+
fn test_vlan_protocol_from_str() {
1370+
use std::str::FromStr;
1371+
assert_eq!(VlanProtocol::Ieee8021Q, "802.1q".parse().unwrap());
1372+
assert_eq!(VlanProtocol::Ieee8021Ad, "802.1ad".parse().unwrap());
1373+
assert_eq!(VlanProtocol::Ieee8021Q, "802.1Q".parse().unwrap());
1374+
assert_eq!(VlanProtocol::Ieee8021Ad, "802.1AD".parse().unwrap());
1375+
assert!(VlanProtocol::from_str("bogus").is_err());
1376+
assert!(VlanProtocol::from_str("802.1Qq").is_err());
1377+
}

src/link/vlan_protocol.rs

Lines changed: 18 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,19 @@ 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!(
67+
"unknown VLAN protocol: {s}"
68+
)))
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)