Skip to content

Commit c094c9e

Browse files
committed
chore: replace unwrap calls with safer alternatives
This replaces calls to `unwrap()` across the crate with safer alternatives. Some patterns observed + updated include: 1. Replacing `is_some() + unwrap()` with `if let Some()`. ``` // Before: if bios.usb_boot.is_some() { let val = bios.usb_boot.unwrap(); // Do something with val. } // After: if let Some(val) = bios.usb_boot { // Do something with val. } ``` 2. Replacing `is_some() + `clone().unwrap() == "value"` with `as_deref() == Some("value")`. ``` // Before: if bios.usb_boot.is_some() && bios.usb_boot.clone().unwrap() == "Disabled" // After (note we can also drop the clone): if bios.usb_boot.as_deref() == Some("Disabled") ``` 3. Replacing `partial_cmp().unwrap()` with `cmp()`. Since `Option<String>` implements `Ord`, we can use `cmp()` and not need `partial_cmp().unwrap()`. ``` // Before: devices.sort_unstable_by(|a, b| a.manufacturer.partial_cmp(&b.manufacturer).unwrap()); // After: devices.sort_unstable_by(|a, b| a.manufacturer.cmp(&b.manufacturer)); ``` 4. Improving `unwrap()` operations that are fallible. We had a number of places where we were just unwrapping options without any error checking at all. This now returns an error with a useful error message in said cases. ``` // Before: let system_id = systems.first().unwrap(); // After: let system_id = systems.first().ok_or_else(|| RedfishError::GenericError { error: "No systems found in service root".to_string(), })?; ``` 5. Improving `unwrap()` operations that are considered infallible. So even for operations we'd consider infallible, like setting the MIME type on an HTTP request, or having our statically compiled regex, we should still properly have an error check in there instead of blindly unwrapping. ``` // Before: .mime_str("application/json").unwrap() // After: .mime_str("application/json") .map_err(|e| RedfishError::GenericError { error: format!("Invalid MIME type 'application/json': {}", e), })? ```
1 parent 031f815 commit c094c9e

9 files changed

Lines changed: 90 additions & 73 deletions

File tree

src/dell.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1921,10 +1921,14 @@ impl Bmc {
19211921
let na_id = match chassis.network_adapters {
19221922
Some(id) => id,
19231923
None => {
1924+
let chassis_odata_url = chassis
1925+
.odata
1926+
.map(|o| o.odata_id)
1927+
.unwrap_or_else(|| "empty_odata_id_url".to_string());
19241928
return Err(RedfishError::MissingKey {
19251929
key: "network_adapters".to_string(),
1926-
url: chassis.odata.unwrap().odata_id,
1927-
})
1930+
url: chassis_odata_url,
1931+
});
19281932
}
19291933
};
19301934

src/hpe.rs

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -338,22 +338,19 @@ impl Redfish for Bmc {
338338
let (_status, bmc): (_, hpe::SetOemHpeLockdown) = self.s.client.get(url.as_str()).await?;
339339
let message = format!(
340340
"usb_boot={}, virtual_nic_enabled={}",
341-
bios.usb_boot.clone().unwrap_or("Unknown".to_string()),
341+
bios.usb_boot.as_deref().unwrap_or("Unknown"),
342342
bmc.oem.hpe.virtual_nic_enabled
343343
);
344344
// todo: kcs_enabled
345345
Ok(Status {
346346
message,
347-
status: if bios.usb_boot.is_some()
348-
&& bios.usb_boot.clone().unwrap() == "Disabled"
347+
status: if bios.usb_boot.as_deref() == Some("Disabled")
349348
&& !bmc.oem.hpe.virtual_nic_enabled
350-
//&& bios.kcs_enabled.is_some() && bios.kcs_enabled.unwrap() == "false"
349+
// todo: && bios.kcs_enabled.as_deref() == Some("false")
351350
{
352351
StatusInternal::Enabled
353-
} else if bios.usb_boot.is_some()
354-
&& bios.usb_boot.clone().unwrap() == "Enabled"
355-
&& bmc.oem.hpe.virtual_nic_enabled
356-
// if bios.usb_boot == "Enabled" && bios.kcs_enabled.clone().is_some() && bios.kcs_enabled.clone().unwrap() == "true"
352+
// todo: if bios.usb_boot.as_deref() == Some("Enabled") && bios.kcs_enabled.as_deref() == Some("true")
353+
} else if bios.usb_boot.as_deref() == Some("Enabled") && bmc.oem.hpe.virtual_nic_enabled
357354
{
358355
StatusInternal::Disabled
359356
} else {
@@ -436,16 +433,15 @@ impl Redfish for Bmc {
436433
async fn pcie_devices(&self) -> Result<Vec<PCIeDevice>, RedfishError> {
437434
let mut out = Vec::new();
438435
let chassis = self.get_chassis(self.s.system_id()).await?;
439-
if chassis.pcie_devices.is_none() {
440-
return Ok(vec![]);
441-
}
442-
let mut devices: Vec<HpePCIeDevice> = Vec::new();
443-
let url = chassis
444-
.pcie_devices
445-
.unwrap()
436+
let pcie_devices_odata = match chassis.pcie_devices {
437+
Some(odata) => odata,
438+
None => return Ok(vec![]),
439+
};
440+
let url = pcie_devices_odata
446441
.odata_id
447442
.replace(&format!("/{REDFISH_ENDPOINT}/"), "");
448443
let pcie_devices = self.s.get_members(&url).await?;
444+
let mut devices: Vec<HpePCIeDevice> = Vec::new();
449445
for pcie_oid in pcie_devices {
450446
let dev_url = format!("{}/{}", &url, pcie_oid);
451447
let (_, hpe_pcie) = self.s.client.get(&dev_url).await?;
@@ -481,7 +477,7 @@ impl Redfish for Bmc {
481477
}
482478
out.push(pcie);
483479
}
484-
out.sort_unstable_by(|a, b| a.manufacturer.partial_cmp(&b.manufacturer).unwrap());
480+
out.sort_unstable_by(|a, b| a.manufacturer.cmp(&b.manufacturer));
485481

486482
Ok(out)
487483
}
@@ -595,12 +591,9 @@ impl Redfish for Bmc {
595591
&self,
596592
chassis_id: &str,
597593
) -> Result<Vec<String>, RedfishError> {
598-
// self.s.get_chassis_network_adapters(chassis_id).await
599594
let chassis = self.s.get_chassis(chassis_id).await?;
600-
if chassis.network_adapters.is_some() {
601-
let url = chassis
602-
.network_adapters
603-
.unwrap()
595+
if let Some(network_adapters_odata) = chassis.network_adapters {
596+
let url = network_adapters_odata
604597
.odata_id
605598
.replace(&format!("/{REDFISH_ENDPOINT}/"), "");
606599
// let url = format!("Chassis/{}/NetworkAdapters", chassis_id);

src/model/power.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ pub struct Power {
191191
impl StatusVec for Power {
192192
fn get_vec(&self) -> Vec<ResourceStatus> {
193193
let mut v: Vec<ResourceStatus> = Vec::new();
194-
if self.power_supplies.is_some() {
195-
for res in self.power_supplies.clone().unwrap() {
196-
v.push(res.status)
194+
if let Some(power_supplies) = &self.power_supplies {
195+
for res in power_supplies {
196+
v.push(res.status);
197197
}
198198
}
199199
v

src/network.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ impl RedfishClientPoolBuilder {
8787
.connect_timeout(self.connect_timeout)
8888
.timeout(self.timeout)
8989
.build()
90-
.unwrap();
90+
.map_err(|e| RedfishError::GenericError {
91+
error: format!("Failed to build RedfishClientPool HTTP client: {}", e),
92+
})?;
9193
let pool = RedfishClientPool { http_client };
9294

9395
Ok(pool)
@@ -162,8 +164,12 @@ impl RedfishClientPool {
162164
let service_root = s.get_service_root().await?;
163165
let systems = s.get_systems().await?;
164166
let managers = s.get_managers().await?;
165-
let system_id = systems.first().unwrap();
166-
let manager_id = managers.first().unwrap();
167+
let system_id = systems.first().ok_or_else(|| RedfishError::GenericError {
168+
error: "No systems found in service root".to_string(),
169+
})?;
170+
let manager_id = managers.first().ok_or_else(|| RedfishError::GenericError {
171+
error: "No managers found in service root".to_string(),
172+
})?;
167173
let chassis = s.get_chassis_all().await?;
168174

169175
s.set_system_id(system_id)?;
@@ -626,14 +632,27 @@ impl RedfishHttpClient {
626632
.part(
627633
"UpdateParameters",
628634
reqwest::multipart::Part::text(parameters)
635+
// mime_str_to_part parses the MIME type. Technically this is
636+
// infallible for known MIME types, including application/json,
637+
// but still check for an error instead of unwrapping.
629638
.mime_str("application/json")
630-
.unwrap(),
639+
.map_err(|e| RedfishError::GenericError {
640+
error: format!("Invalid MIME type 'application/json': {}", e),
641+
})?,
631642
)
632643
.part(
633644
"UpdateFile",
634645
Part::stream_with_length(file, length)
646+
// mime_str_to_part parses the MIME type. Technically this is
647+
// infallible for known MIME types, including application/octet-stream,
648+
// but still check for an error instead of unwrapping.
635649
.mime_str("application/octet-stream")
636-
.unwrap()
650+
.map_err(|e| RedfishError::GenericError {
651+
error: format!(
652+
"Invalid MIME type 'application/octet-stream': {}",
653+
e
654+
),
655+
})?
637656
// Yes, the filename passed does matter for some reason, at least for Dells, and it has to be the basename.
638657
.file_name(basename.clone()),
639658
),

src/nvidia_gbswitch.rs

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -358,25 +358,23 @@ impl Redfish for Bmc {
358358
for id in devices.members {
359359
url = id.odata_id.replace(&format!("/{REDFISH_ENDPOINT}/"), "");
360360
let p: PCIeDevice = self.s.client.get(&url).await?.1;
361-
if p.id.is_none()
362-
|| p.status.is_none()
363-
|| !p
364-
.status
365-
.as_ref()
366-
.unwrap()
367-
.state
368-
.as_ref()
369-
.unwrap()
370-
.to_lowercase()
371-
.contains("enabled")
372-
{
361+
// To be considered enabled, the PCIE device needs to have
362+
// an ID, a status, and the status needs to be enabled.
363+
let is_device_enabled = p.id.is_some()
364+
&& p.status.as_ref().is_some_and(|s| {
365+
s.state
366+
.as_ref()
367+
.is_some_and(|state| state.to_lowercase().contains("enabled"))
368+
});
369+
if !is_device_enabled {
373370
continue;
374371
}
375372
out.push(p);
376373
}
377374
}
378375
}
379-
out.sort_unstable_by(|a, b| a.manufacturer.partial_cmp(&b.manufacturer).unwrap());
376+
377+
out.sort_unstable_by(|a, b| a.manufacturer.cmp(&b.manufacturer));
380378
Ok(out)
381379
}
382380

src/nvidia_gbx00.rs

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,19 @@ impl Display for BootOptionMatchField {
109109
// Supported component to firmware mapping.
110110
// GPU, Source: HGX_IRoT_GPU_X Target: HGX_FW_GPU_X
111111
fn get_component_integrity_id_to_firmware_inventory_id_options(
112-
) -> &'static Vec<RegexToFirmwareIdOptions> {
113-
static RE: OnceLock<Vec<RegexToFirmwareIdOptions>> = OnceLock::new();
112+
) -> Result<&'static Vec<RegexToFirmwareIdOptions>, RedfishError> {
113+
static RE: OnceLock<Result<Vec<RegexToFirmwareIdOptions>, String>> = OnceLock::new();
114114
RE.get_or_init(|| {
115-
vec![RegexToFirmwareIdOptions {
115+
Ok(vec![RegexToFirmwareIdOptions {
116116
id_prefix: "HGX_FW_",
117-
pattern: Regex::new(r"HGX_IRoT_(GPU_\d+)").unwrap(),
118-
}]
117+
// Assuming our static pattern is good, this is probably
118+
// safe, but still check for an error instead of unwrapping.
119+
pattern: Regex::new(r"HGX_IRoT_(GPU_\d+)").map_err(|e| e.to_string())?,
120+
}])
121+
})
122+
.as_ref()
123+
.map_err(|e| RedfishError::GenericError {
124+
error: format!("Failed to compile regex: {}", e),
119125
})
120126
}
121127

@@ -1039,7 +1045,7 @@ impl Redfish for Bmc {
10391045
) -> Result<crate::model::software_inventory::SoftwareInventory, RedfishError> {
10401046
let mut id = None;
10411047

1042-
for value in get_component_integrity_id_to_firmware_inventory_id_options() {
1048+
for value in get_component_integrity_id_to_firmware_inventory_id_options()? {
10431049
if let Some(capture) = value.pattern.captures(component_integrity_id) {
10441050
id = Some(format!(
10451051
"{}{}",

src/nvidia_viking.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1226,8 +1226,7 @@ impl Bmc {
12261226
let bios = self.get_bios().await?;
12271227
let bios = bios.attributes;
12281228

1229-
if bios.acpi_spcr_console_redirection_enable.is_some() {
1230-
let val = bios.acpi_spcr_console_redirection_enable.unwrap();
1229+
if let Some(val) = bios.acpi_spcr_console_redirection_enable {
12311230
message.push_str(&format!("acpi_spcr_console_redirection_enable={val} "));
12321231
match val {
12331232
true => {
@@ -1240,8 +1239,7 @@ impl Bmc {
12401239
}
12411240
}
12421241
}
1243-
if bios.console_redirection_enable0.is_some() {
1244-
let val = bios.console_redirection_enable0.unwrap();
1242+
if let Some(val) = bios.console_redirection_enable0 {
12451243
message.push_str(&format!("console_redirection_enable0={val} "));
12461244
match val {
12471245
true => {
@@ -1255,30 +1253,26 @@ impl Bmc {
12551253
// All of these need a specific value for serial console access to work.
12561254
// Any other value counts as correctly disabled.
12571255

1258-
if bios.acpi_spcr_port.is_some() {
1259-
let val = bios.acpi_spcr_port.unwrap();
1256+
if let Some(val) = &bios.acpi_spcr_port {
12601257
message.push_str(&format!("acpi_spcr_port={val} "));
12611258
if val != DEFAULT_ACPI_SPCR_PORT {
12621259
enabled = false;
12631260
}
12641261
}
1265-
if bios.acpi_spcr_flow_control.is_some() {
1266-
let val = bios.acpi_spcr_flow_control.unwrap();
1262+
if let Some(val) = &bios.acpi_spcr_flow_control {
12671263
message.push_str(&format!("acpi_spcr_flow_control={val} "));
12681264
if val != DEFAULT_ACPI_SPCR_FLOW_CONTROL {
12691265
enabled = false;
12701266
}
12711267
}
1272-
if bios.acpi_spcr_baud_rate.is_some() {
1273-
let val = bios.acpi_spcr_baud_rate.unwrap();
1268+
if let Some(val) = &bios.acpi_spcr_baud_rate {
12741269
message.push_str(&format!("acpi_spcr_baud_rate={val} "));
12751270
if val != DEFAULT_ACPI_SPCR_BAUD_RATE {
12761271
enabled = false;
12771272
}
12781273
}
12791274

1280-
if bios.baud_rate0.is_some() {
1281-
let val = bios.baud_rate0.unwrap();
1275+
if let Some(val) = &bios.baud_rate0 {
12821276
message.push_str(&format!("baud_rate0={val} "));
12831277
if val != DEFAULT_BAUD_RATE0 {
12841278
enabled = false;
@@ -1318,7 +1312,7 @@ impl Bmc {
13181312
//
13191313
// TODO: Many BootOptions have Alias="Pxe". This probably isn't doing what we want.
13201314
//
1321-
if b.alias.is_some() && b.alias.unwrap() == with_name_str {
1315+
if b.alias.as_deref() == Some(&with_name_str) {
13221316
ordered.insert(0, format!("Boot{}", b.id).to_string());
13231317
continue;
13241318
}

src/standard.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -615,20 +615,22 @@ impl Redfish for RedfishStandard {
615615
if systems.members.is_empty() {
616616
return Ok(vec!["1".to_string()]); // default to DMTF standard suggested
617617
}
618-
let v = systems
618+
let v: Result<Vec<String>, RedfishError> = systems
619619
.members
620620
.into_iter()
621621
.map(|d| {
622622
d.odata_id
623623
.trim_matches('/')
624624
.split('/')
625625
.next_back()
626-
.unwrap()
627-
.to_string()
626+
.map(|s| s.to_string())
627+
.ok_or_else(|| RedfishError::GenericError {
628+
error: format!("Invalid odata_id format: {}", d.odata_id),
629+
})
628630
})
629631
.collect();
630632

631-
Ok(v)
633+
v
632634
}
633635

634636
async fn get_manager(&self) -> Result<Manager, RedfishError> {
@@ -644,19 +646,21 @@ impl Redfish for RedfishStandard {
644646
if bmcs.members.is_empty() {
645647
return Ok(vec!["1".to_string()]);
646648
}
647-
let v: Vec<String> = bmcs
649+
let v: Result<Vec<String>, RedfishError> = bmcs
648650
.members
649651
.into_iter()
650652
.map(|d| {
651653
d.odata_id
652654
.trim_matches('/')
653655
.split('/')
654656
.next_back()
655-
.unwrap()
656-
.to_string()
657+
.map(|s| s.to_string())
658+
.ok_or_else(|| RedfishError::GenericError {
659+
error: format!("Invalid odata_id format: {}", d.odata_id),
660+
})
657661
})
658662
.collect();
659-
Ok(v)
663+
v
660664
}
661665

662666
async fn bmc_reset_to_defaults(&self) -> Result<(), RedfishError> {
@@ -1369,7 +1373,7 @@ impl RedfishStandard {
13691373
devices.extend(chassis_devices);
13701374
}
13711375

1372-
devices.sort_unstable_by(|a, b| a.manufacturer.partial_cmp(&b.manufacturer).unwrap());
1376+
devices.sort_unstable_by(|a, b| a.manufacturer.cmp(&b.manufacturer));
13731377
Ok(devices)
13741378
}
13751379
}

tests/integration_test.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,4 +806,3 @@ fn get_tmp_dir() -> PathBuf {
806806
let temp_dir = format!("{}-{}-{}", PYTHON_VENV_DIR, std::process::id(), nanos);
807807
env::temp_dir().join(&temp_dir)
808808
}
809-

0 commit comments

Comments
 (0)