Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions relay-event-normalization/src/eap/trimming.rs
Original file line number Diff line number Diff line change
Expand Up @@ -716,8 +716,7 @@ mod tests {
// That leaves 9B for the string's value.
// Note that the `number` field doesn't take up any size.
// The `"footer"` is removed because it comes after the attributes and there's no space left.
let state =
ProcessingState::new_root(Some(Cow::Owned(FieldAttrs::default().max_bytes(50))), []);
let state = ProcessingState::root_builder().max_bytes(50).build();
processor::process_value(&mut value, &mut processor, &state).unwrap();

insta::assert_json_snapshot!(SerializableAnnotated(&value), @r###"
Expand Down Expand Up @@ -883,8 +882,7 @@ mod tests {
});

let mut processor = TrimmingProcessor::new(100);
let state =
ProcessingState::new_root(Some(Cow::Owned(FieldAttrs::default().max_bytes(30))), []);
let state = ProcessingState::root_builder().max_bytes(30).build();
processor::process_value(&mut value, &mut processor, &state).unwrap();

insta::assert_json_snapshot!(SerializableAnnotated(&value), @r###"
Expand Down Expand Up @@ -937,8 +935,7 @@ mod tests {

let mut attributes = Annotated::new(attributes);

let state =
ProcessingState::new_root(Some(Cow::Owned(FieldAttrs::default().max_bytes(40))), []);
let state = ProcessingState::root_builder().max_bytes(40).build();
processor::process_value(&mut attributes, &mut TrimmingProcessor::new(20), &state).unwrap();
let attributes_after_trimming = attributes.clone();
processor::process_value(&mut attributes, &mut TrimmingProcessor::new(20), &state).unwrap();
Expand Down
119 changes: 114 additions & 5 deletions relay-event-schema/src/processor/attrs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ impl FieldAttrs {
self
}

/// Sets whether this field can have an empty value.
/// Sets whether this field's value must be nonempty.
///
/// This is distinct from `required`. An empty string (`""`) passes the "required" check but not the
/// "nonempty" one.
Expand Down Expand Up @@ -239,8 +239,8 @@ impl FieldAttrs {
}

/// Sets the maximum number of characters allowed in the field.
pub const fn max_chars(mut self, max_chars: usize) -> Self {
self.max_chars = SizeMode::Static(Some(max_chars));
pub const fn max_chars(mut self, max_chars: Option<usize>) -> Self {
self.max_chars = SizeMode::Static(max_chars);
self
}

Expand All @@ -254,8 +254,8 @@ impl FieldAttrs {
}

/// Sets the maximum number of bytes allowed in the field.
pub const fn max_bytes(mut self, max_bytes: usize) -> Self {
self.max_bytes = SizeMode::Static(Some(max_bytes));
pub const fn max_bytes(mut self, max_bytes: Option<usize>) -> Self {
self.max_bytes = SizeMode::Static(max_bytes);
self
}

Expand Down Expand Up @@ -355,6 +355,96 @@ impl<T> Deref for BoxCow<'_, T> {
}
}

/// A builder for root [`ProcessingStates`](ProcessingState).
///
/// This is created by [`ProcessingState::root_builder`].
#[derive(Debug, Clone)]
pub struct ProcessingStateBuilder {
attrs: Option<FieldAttrs>,
value_type: EnumSet<ValueType>,
}

impl ProcessingStateBuilder {
/// Modifies the attributes of the root field.
pub fn attrs<F: FnOnce(FieldAttrs) -> FieldAttrs>(mut self, f: F) -> Self {
let attrs = self.attrs.take().unwrap_or_default();
self.attrs = Some(f(attrs));
self
}

/// Sets whether a value in the root field is required.
pub fn required(self, required: bool) -> Self {
self.attrs(|attrs| attrs.required(required))
}

/// Sets whether the root field's value must be nonempty.
///
/// This is distinct from `required`. An empty string (`""`) passes the "required" check but not the
/// "nonempty" one.
pub fn nonempty(self, nonempty: bool) -> Self {
self.attrs(|attrs| attrs.nonempty(nonempty))
}

/// Sets whether whitespace should be trimmed on the root field before validation.
pub fn trim_whitespace(self, trim_whitespace: bool) -> Self {
self.attrs(|attrs| attrs.trim_whitespace(trim_whitespace))
}

/// Sets whether the root field contains PII.
pub fn pii(self, pii: Pii) -> Self {
self.attrs(|attrs| attrs.pii(pii))
}

/// Sets whether the root field contains PII dynamically based on the current state.
pub fn pii_dynamic(self, pii: fn(&ProcessingState) -> Pii) -> Self {
self.attrs(|attrs| attrs.pii_dynamic(pii))
}

/// Sets the maximum number of chars allowed in the root field.
pub fn max_chars(self, max_chars: impl Into<Option<usize>>) -> Self {
self.attrs(|attrs| attrs.max_chars(max_chars.into()))
}

/// Sets the maximum number of characters allowed in the root field dynamically based on the current state.
pub fn max_chars_dynamic(self, max_chars: fn(&ProcessingState) -> Option<usize>) -> Self {
self.attrs(|attrs| attrs.max_chars_dynamic(max_chars))
}

/// Sets the maximum number of bytes allowed in the root field.
pub fn max_bytes(self, max_bytes: impl Into<Option<usize>>) -> Self {
self.attrs(|attrs| attrs.max_bytes(max_bytes.into()))
}

/// Sets the maximum number of bytes allowed in the root field dynamically based on the current state.
pub fn max_bytes_dynamic(self, max_bytes: fn(&ProcessingState) -> Option<usize>) -> Self {
self.attrs(|attrs| attrs.max_bytes_dynamic(max_bytes))
}

/// Sets whether additional properties should be retained during normalization.
pub fn retain(self, retain: bool) -> Self {
self.attrs(|attrs| attrs.retain(retain))
}

/// Sets the value type for the root state.
pub fn value_type(mut self, value_type: EnumSet<ValueType>) -> Self {
self.value_type = value_type;
self
}

/// Consumes the builder and returns a root [`ProcessingState`] with
/// the configured attributes and value type.
pub fn build(self) -> ProcessingState<'static> {
let Self { attrs, value_type } = self;
ProcessingState {
parent: None,
path_item: None,
attrs: attrs.map(Cow::Owned),
value_type,
depth: 0,
}
}
}

/// An event's processing state.
///
/// The processing state describes an item in an event which is being processed, an example
Expand Down Expand Up @@ -405,6 +495,25 @@ impl<'a> ProcessingState<'a> {
}
}

/// Creates a builder that can be used to easily create
/// a custom root state.
///
/// # Example
/// ```
/// use relay_event_schema::processor::ProcessingState;
///
/// let root = ProcessingState::root_builder()
/// .max_bytes(50)
/// .retain(true)
/// .build();
/// ```
pub fn root_builder() -> ProcessingStateBuilder {
ProcessingStateBuilder {
attrs: None,
value_type: EnumSet::empty(),
}
}

/// Derives a processing state by entering a borrowed key.
pub fn enter_borrowed(
&'a self,
Expand Down
Loading