Move IfNonEmpty trait to carbide-utils, migrate stuff to it#3482
Conversation
We've rewritten similar helper functions in a lot of places to convert empty strings (or Option<String>::None) to None. Use the trait-based one (IfNonEmpty), move it to carbide-utils, and migrate code to use it. (I needed this again in another branch and I was sick of writing it yet-another time.)
WalkthroughA shared ChangesEmpty-value normalization
Estimated code review effort: 3 (Moderate) | ~25 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (2)
crates/utils/src/none_if_empty.rs (2)
20-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffRepetitive impl boilerplate could be macro-generated.
All ten
NoneIfEmptyimpls follow one of two fixed shapes (if self.is_empty() {None} else {Some(self)}for owned/borrowed non-Optiontypes, and empty-filtering forOption<_>wrappers). A small declarative macro could generate these pairs and reduce ~80 lines to a fraction of that, while keeping the public trait surface identical. Given the guideline to prefer simple, explicit code and to only introduce abstractions when justified, this is optional — the current form is easy to read and audit — but worth considering if more type families get added later.As per coding guidelines, "Prefer simple, explicit Rust code over clever or heavily abstracted code; abstractions should be justified by a real requirement."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/utils/src/none_if_empty.rs` around lines 20 - 104, The requested change is optional: keep the explicit NoneIfEmpty implementations unless additional type families justify reducing duplication. If consolidating, use a small declarative macro to generate the owned/borrowed and Option-wrapped implementations while preserving the public NoneIfEmpty trait and all existing empty-value behavior.Source: Coding guidelines
26-104: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConsider unifying the
Option<T>impls via.filter().
Option<String>(Line 30) andOption<&str>(Line 88) use.filter(|value| !value.is_empty()), whileOption<Vec<T>>(Lines 45-49) andOption<HashMap<K, V>>(Lines 63-67) use amatchwith a guard for the same semantics.Option<&[T]>(Line 102) reverts back to.filter(). Unifying all fiveOption<_>impls to the.filter()form would remove the stylistic inconsistency without any behavioral change.♻️ Proposed fix for consistency
impl<T> NoneIfEmpty for Option<Vec<T>> { type Val = Vec<T>; fn none_if_empty(self) -> Option<Self::Val> { - match self { - None => None, - Some(v) if v.is_empty() => None, - Some(v) => Some(v), - } + self.filter(|v| !v.is_empty()) } } impl<K, V> NoneIfEmpty for Option<HashMap<K, V>> { type Val = HashMap<K, V>; fn none_if_empty(self) -> Option<Self::Val> { - match self { - None => None, - Some(h) if h.is_empty() => None, - Some(h) => Some(h), - } + self.filter(|h| !h.is_empty()) } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/utils/src/none_if_empty.rs` around lines 26 - 104, Unify the Option-based implementations of NoneIfEmpty by updating none_if_empty in Option<Vec<T>> and Option<HashMap<K, V>> to use the same filter-based pattern already used by Option<String>, Option<&str>, and Option<&[T]>. Preserve the existing behavior of returning None for absent or empty values and Some for non-empty values.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@crates/utils/src/none_if_empty.rs`:
- Around line 20-104: The requested change is optional: keep the explicit
NoneIfEmpty implementations unless additional type families justify reducing
duplication. If consolidating, use a small declarative macro to generate the
owned/borrowed and Option-wrapped implementations while preserving the public
NoneIfEmpty trait and all existing empty-value behavior.
- Around line 26-104: Unify the Option-based implementations of NoneIfEmpty by
updating none_if_empty in Option<Vec<T>> and Option<HashMap<K, V>> to use the
same filter-based pattern already used by Option<String>, Option<&str>, and
Option<&[T]>. Preserve the existing behavior of returning None for absent or
empty values and Some for non-empty values.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 06cc05b5-617b-42a5-89b8-fda8de8af302
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (48)
crates/admin-cli/src/machine/nvlink_info/cmd.rscrates/admin-cli/src/managed_switch/show/cmd.rscrates/admin-cli/src/site_explorer/get_report/cmd.rscrates/admin-cli/src/sku/bulk_update_metadata/cmd.rscrates/agent/src/astra_weave.rscrates/agent/src/dpu/interface.rscrates/agent/src/ethernet_virtualization.rscrates/api-core/src/cfg/file.rscrates/api-core/src/ethernet_virtualization.rscrates/api-core/src/handlers/bmc_endpoint_explorer.rscrates/api-core/src/handlers/component_manager.rscrates/api-core/src/handlers/extension_service.rscrates/api-core/src/handlers/machine_discovery.rscrates/api-core/src/handlers/machine_identity.rscrates/api-core/src/handlers/tenant_identity_config.rscrates/api-core/src/machine_identity/token_exchange.rscrates/api-core/src/setup.rscrates/api-db/src/machine_validation_suites.rscrates/api-model/src/instance/status/extension_service.rscrates/api-model/src/machine_boot_interface.rscrates/api-model/src/site_explorer/mod.rscrates/dpf/Cargo.tomlcrates/dpf/src/sdk.rscrates/dpu-remediation/Cargo.tomlcrates/dpu-remediation/src/remediation.rscrates/health/src/collectors/nvue/gnmi/sample_processor.rscrates/health/src/endpoint/sources.rscrates/machine-controller/src/handler/firmware_artifact.rscrates/rack-controller/src/fabric_manager.rscrates/rack-controller/src/maintenance.rscrates/rack-controller/src/nmx_certificate.rscrates/rpc-utils/Cargo.tomlcrates/rpc-utils/src/managed_host_display.rscrates/rpc/src/libmlx.rscrates/rpc/src/model/compute_allocation.rscrates/rpc/src/model/expected_switch.rscrates/rpc/src/model/instance_type.rscrates/rpc/src/model/machine_validation.rscrates/rpc/src/model/metadata.rscrates/rpc/src/model/network_security_group.rscrates/rpc/src/model/vpc.rscrates/scout/src/firmware_upgrade.rscrates/site-explorer/src/lib.rscrates/site-explorer/src/machine_creator.rscrates/switch-controller/src/endpoint.rscrates/switch-controller/src/maintenance.rscrates/utils/src/lib.rscrates/utils/src/none_if_empty.rs
🔍 Container Scan Summary
Per-CVE detail lives in the per-service |
We've rewritten similar helper functions in a lot of places to convert empty strings (or Option::None) to None. Use the trait-based one (IfNonEmpty), rename it NoneIfEmpty, move it to carbide-utils, and migrate code to use it. (I needed this again in another branch and I was sick of writing it yet-another time.)
Type of Change
Breaking Changes
Testing
Additional Notes