Skip to content

Builder APIs for BundleDataSelection and EreportFilters#10108

Merged
mergeconflict merged 1 commit intomainfrom
mergeconflict/builder-side-quest
Mar 20, 2026
Merged

Builder APIs for BundleDataSelection and EreportFilters#10108
mergeconflict merged 1 commit intomainfrom
mergeconflict/builder-side-quest

Conversation

@mergeconflict
Copy link
Copy Markdown
Contributor

@mergeconflict mergeconflict commented Mar 19, 2026

Address @hawkw feedback from #10090 and #10089:

  • EreportFilters should have a builder API that validates datetime ranges.
  • BundleDataSelection should have a builder API that makes it more ergonomic and hides storage details.

@mergeconflict mergeconflict changed the base branch from main to mergeconflict/fm-sb-types March 19, 2026 22:51
@mergeconflict mergeconflict force-pushed the mergeconflict/builder-side-quest branch from 0f23c32 to 8d11f55 Compare March 19, 2026 22:57
Base automatically changed from mergeconflict/fm-sb-types to main March 19, 2026 23:02
@mergeconflict mergeconflict force-pushed the mergeconflict/builder-side-quest branch 2 times, most recently from 9d21ce4 to cbc48df Compare March 19, 2026 23:18
@mergeconflict mergeconflict changed the title EreportFilters: make fields private, add builder API Builder APIs for BundleDataSelection and EreportFilters Mar 19, 2026
@mergeconflict mergeconflict requested review from hawkw and smklein March 19, 2026 23:24
@mergeconflict mergeconflict force-pushed the mergeconflict/builder-side-quest branch 3 times, most recently from e8dec52 to a644452 Compare March 20, 2026 16:10
///
/// ```
/// # use nexus_types::fm::ereport::EreportFilters;
/// let filters = EreportFilters::new()
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat, this usage looks a lot nicer!

/// Builder-style method that inserts a [`BundleData`] value and returns
/// `self`. If multiple BundleData entries with the same type are inserted,
/// the last write wins.
pub fn with(mut self, bundle_data: BundleData) -> Self {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this should be a non-pub method? Seems covered by all the other helpers now, right?

(If we can provide the same functionality with "one path", seems nice to not have "two pub paths" that are equivalent)

Copy link
Copy Markdown
Contributor Author

@mergeconflict mergeconflict Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had it that way initially, but it turns out to be handy. I get to do this sort of thing:

    fn from_iter<T: IntoIterator<Item = BundleData>>(iter: T) -> Self {
        iter.into_iter().fold(Self::new(), |sel, data| sel.with(data))
    }

rather than having to match on the BundleDataCategory and call the specific with_ variant.

Comment thread nexus/types/src/fm/ereport.rs Outdated
/// Note: deserialization bypasses builder validation, so a deserialized
/// `EreportFilters` may have `start_time > end_time`. This is acceptable
/// for trusted internal use (e.g. JSON stored in
/// [`BundleDataSelection`](crate::support_bundle::BundleDataSelection)).
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Normally I'd be more worried about this but the failure mode here would be "the database returns invalid start/end times, we ask for an empty / impossible period for our filters, so the returned set is empty", which doesn't seem so bad.

If we foresee other classes of errors here it might be worth it to actually do this validation on construction and deserialization?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer the deserialization to handle this, but 🤷‍♀️

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, 770be0e.

Copy link
Copy Markdown
Member

@hawkw hawkw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had some thoughts about the APIs based on what I can kind of imagine DEs wanting to be able to do eventually --- basically, that it could be helpful to build bundles a bit more bit-by-bit than we are currently doing. It's fine if we don't really want to go change up the API now, though; we could add stuff like that later if it becomes necessary later.

Comment thread nexus/types/src/fm/ereport.rs
Comment on lines +308 to +320
pub fn with_serials(
mut self,
serials: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.only_serials.extend(serials.into_iter().map(Into::into));
self
}

/// Adds ereport classes to the inclusion filter.
///
/// When one or more classes are present, only ereports with those
/// class strings are included.
pub fn with_classes(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that I would kinda like the naming here to make it a bit more obvious that they are saying "include only these serials/classes" (which is why the fields are called "only_classes"/"only_serials"), but I can't really think of something that would make that clearer while not being grammatically super awkward as a builder-y "with" method, while not also incorrectly implying that if you call it multiple times, it clobbers the previous thing. Hm. I guess the current names are fine.

Comment thread nexus/types/src/support_bundle.rs
Comment thread nexus/types/src/support_bundle.rs
Comment thread nexus/types/src/fm/ereport.rs Outdated
/// Note: deserialization bypasses builder validation, so a deserialized
/// `EreportFilters` may have `start_time > end_time`. This is acceptable
/// for trusted internal use (e.g. JSON stored in
/// [`BundleDataSelection`](crate::support_bundle::BundleDataSelection)).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also prefer the deserialization to handle this, but 🤷‍♀️

Comment on lines +304 to +314
/// Adds serial numbers to the inclusion filter.
///
/// When one or more serials are present, only ereports reported by
/// systems with those serial numbers are included.
pub fn with_serials(
mut self,
serials: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
self.only_serials.extend(serials.into_iter().map(Into::into));
self
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this is COMPLETELY UNRELATED TO YOUR CHANGE AND DOES NOT NEED TO BE FIXED HERE, but I am realizing that the way we are currently querying for ereports by serial is a bit wrong. V1 serials are only guaranteed to be unique within a part number, so we may see (for example) a sled and switch with the same serial. Which means we may get surprising results from an ereport query --- if the user wanted to get ereports from 0XV1:913-0000019:4:BRM42000069 (a Gimlet) and the rack also contained OXV1:913-0000003:2:BRM42000069 (a PSC), they might be somewhat surprised to discover that the bundle they requested of the Gimlet's ereports also has a bunch of "rectifier inserted and removed here" ereports from the PSC. So, we should probably change the way we query ereports by serial to also include the CPN. Sigh. This one's my fault.

@mergeconflict mergeconflict force-pushed the mergeconflict/builder-side-quest branch 2 times, most recently from 770be0e to 8153e6f Compare March 20, 2026 17:57
@mergeconflict mergeconflict force-pushed the mergeconflict/builder-side-quest branch from 8153e6f to c171063 Compare March 20, 2026 18:08
@mergeconflict mergeconflict enabled auto-merge (squash) March 20, 2026 18:38
@mergeconflict mergeconflict merged commit e7d260a into main Mar 20, 2026
16 checks passed
@mergeconflict mergeconflict deleted the mergeconflict/builder-side-quest branch March 20, 2026 20:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants