proto_types/protovalidate/
violations.rs

1use crate::protovalidate::{FieldPath, FieldPathElement, Violation, Violations};
2
3impl FieldPath {
4  /// Returns the last member in the elements list, if the list is not empty.
5  pub fn last_field(&self) -> Option<&FieldPathElement> {
6    if let Some(last_field) = self.elements.last() {
7      return Some(last_field);
8    }
9    None
10  }
11
12  /// Returns the second last member in the elements list, if the list is not empty.
13  pub fn parent_field(&self) -> Option<&FieldPathElement> {
14    let second_last = self.elements.get(self.elements.len().wrapping_sub(2));
15
16    match second_last {
17      Some(el) => Some(el),
18      None => None,
19    }
20  }
21
22  /// Checks if the elements list is empty or not.
23  pub fn has_fields(&self) -> bool {
24    self.last_field().is_some()
25  }
26
27  /// Returns the name of the last member in the elements list, if there is one.
28  pub fn last_field_name(&self) -> Option<&str> {
29    self.last_field().map(|f| f.field_name())
30  }
31
32  /// Searches for a FieldPathElement by name in the elements list.
33  pub fn get_field(&self, name: &str) -> Option<&FieldPathElement> {
34    self
35      .elements
36      .iter()
37      .find(|&field| field.field_name() == name)
38  }
39
40  /// Returns a vector with the names from each element in the list.
41  pub fn field_path(&self) -> Vec<String> {
42    let mut path: Vec<String> = Vec::new();
43
44    for field in self.elements.iter() {
45      path.push(field.field_name().to_string());
46
47      if let Some(key) = &field.subscript {
48        path.push(key.to_string());
49      }
50    }
51
52    path
53  }
54
55  /// Returns all of the names from each path element, joined by a dot (e.g. `person.friend.0.address.street_name`)
56  pub fn field_path_str(&self) -> String {
57    self.field_path().join(".")
58  }
59}
60
61impl Violations {
62  /// Searches for a violation with a specific rule id.
63  pub fn violation_by_rule_id(&self, rule_id: &str) -> Option<&Violation> {
64    self.violations.iter().find(|v| v.rule_id() == rule_id)
65  }
66}
67
68impl Violation {
69  /// Returns the last member in the elements list, if there is one.
70  pub fn last_field(&self) -> Option<&FieldPathElement> {
71    if let Some(fields) = &self.field {
72      return fields.last_field();
73    }
74
75    None
76  }
77
78  /// Returns the second last member in the elements list, if there is one.
79  pub fn parent_field(&self) -> Option<&FieldPathElement> {
80    if let Some(fields) = &self.field {
81      return fields.parent_field();
82    }
83
84    None
85  }
86
87  /// Searches for a field in the FieldPath list with a specific name.
88  pub fn get_field(&self, name: &str) -> Option<&FieldPathElement> {
89    if let Some(fields) = &self.field {
90      return fields.get_field(name);
91    }
92
93    None
94  }
95
96  /// If the FieldPath is present, it will return the list of the names for each path element.
97  pub fn field_path(&self) -> Option<Vec<String>> {
98    if let Some(fields) = &self.field {
99      return Some(fields.field_path());
100    }
101
102    None
103  }
104
105  /// Returns the element names composing the violation's rule, like ["string", "max_len"].
106  pub fn rule_path(&self) -> Option<Vec<String>> {
107    if let Some(rules) = &self.rule {
108      return Some(rules.field_path());
109    }
110
111    None
112  }
113
114  /// If there is a FieldPath, it returns the path elements' names, joined by a dot (e.g. `person.friend.0.address.street_name`).
115  pub fn field_path_str(&self) -> Option<String> {
116    if let Some(fields) = &self.field {
117      return Some(fields.field_path_str());
118    }
119
120    None
121  }
122
123  /// If a rule path is defined, it returns the rule path segments for this violation, joined by a dot (e.g. `map.keys.string.min_len`)
124  pub fn rule_path_str(&self) -> Option<String> {
125    if let Some(rules) = &self.rule {
126      return Some(rules.field_path_str());
127    }
128
129    None
130  }
131
132  /// Checks whether this violation has a FieldPath or not. This may not be the case when a violation is triggered by a rule defined with (buf.validate.message).cel in a message
133  pub fn has_fields(&self) -> bool {
134    self.last_field().is_some()
135  }
136
137  /// Checks if the list of FieldPathElements contains a field with a particular name.
138  pub fn has_field_by_name(&self, name: &str) -> bool {
139    self.get_field(name).is_some()
140  }
141
142  /// If a list of path elements is defined, it returns the name of the invalid field (the last field in the list of path elements)
143  pub fn field_name(&self) -> Option<&str> {
144    self.last_field().map(|f| f.field_name())
145  }
146}