• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

input-output-hk / catalyst-libs / 16648343345

31 Jul 2025 11:59AM UTC coverage: 66.061% (-1.3%) from 67.341%
16648343345

push

github

web-flow
feat(general): `rbac-registration` version `0.0.6` (#452)

* bump rbac-registration version

* fix

11097 of 16798 relevant lines covered (66.06%)

2374.96 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

48.45
/rust/catalyst-types/src/problem_report.rs
1
//! Problem Report type
2
//!
3
//! Problem reports are "soft errors" that indicate an issue with the type that holds
4
//! them. They are not "hard errors" that prevent processing, but are intended to capture
5
//! a list of issues related that may be fixed by the user.
6

7
use std::sync::Arc;
8

9
use orx_concurrent_vec::ConcurrentVec;
10
use serde::Serialize;
11

12
/// The kind of problem being reported
13
#[derive(Debug, Serialize, Clone)]
14
#[serde(tag = "type")]
15
enum Kind {
16
    /// Expected and Required field is missing
17
    MissingField {
18
        /// Name of the missing field
19
        field: String,
20
    },
21
    /// Unknown and unexpected field was detected
22
    UnknownField {
23
        /// field name
24
        field: String,
25
        /// the value of the field
26
        value: String,
27
    },
28
    /// Expected Field contains invalid value (Field Name, Found Value, Constraints)
29
    InvalidValue {
30
        /// Name of the field with an invalid value
31
        field: String,
32
        /// The detected invalid value
33
        value: String,
34
        /// The constraint of what is expected for a valid value
35
        constraint: String,
36
    },
37
    /// Expected Field was encoded incorrectly
38
    InvalidEncoding {
39
        /// Name of the invalidly encoded field
40
        field: String,
41
        /// Detected encoding
42
        encoded: String,
43
        /// Expected encoding
44
        expected: String,
45
    },
46
    /// Problem with functional validation, typically cross field validation
47
    FunctionalValidation {
48
        /// Explanation of the failed or problematic validation
49
        explanation: String,
50
    },
51
    /// Duplicate field was detected.
52
    DuplicateField {
53
        /// The duplicated field.
54
        field: String,
55
        /// Additional information about the duplicate field.
56
        description: String,
57
    },
58
    /// Conversion error.
59
    ConversionError {
60
        /// The field that failed to convert
61
        field: String,
62
        /// The value that failed to convert
63
        value: String,
64
        /// The type that the value was expected to convert to
65
        expected_type: String,
66
    },
67
    /// An uncategorized problem was encountered. Use only for rare problems, otherwise
68
    /// make a new problem kind.
69
    Other {
70
        /// A description of the problem
71
        description: String,
72
    },
73
}
74

75
/// Problem Report Entry
76
#[derive(Debug, Serialize, Clone)]
77
struct Entry {
78
    /// The kind of problem we are recording.
79
    kind: Kind,
80
    /// Any extra context information we want to add.
81
    context: String,
82
}
83

84
/// The Problem Report list
85
#[derive(Debug, Clone, Serialize)]
86
struct Report(ConcurrentVec<Entry>);
87

88
/// An inner state of the report.
89
#[derive(Debug, Serialize)]
90
struct State {
91
    /// What context does the whole report have
92
    context: String,
93
    /// The report itself
94
    report: Report,
95
}
96

97
/// Problem Report.
98
///
99
/// This structure allows making a cheap copies that share the same state.
100
#[derive(Debug, Clone, Serialize)]
101
pub struct ProblemReport(Arc<State>);
102

103
impl ProblemReport {
104
    /// Creates a new `ProblemReport` with the given context string.
105
    ///
106
    /// # Arguments
107
    /// * `context`: A reference to a string slice that is used as the context for the
108
    ///   problem report.
109
    ///
110
    /// # Returns
111
    /// A new instance of `ProblemReport`.
112
    ///
113
    /// # Examples
114
    /// ```rust
115
    /// # use catalyst_types::problem_report::ProblemReport;
116
    /// let report = ProblemReport::new("RBAC Registration Decoding");
117
    /// ```
118
    #[must_use]
119
    pub fn new(context: &str) -> Self {
2✔
120
        let state = State {
2✔
121
            context: context.to_owned(),
2✔
122
            report: Report(ConcurrentVec::new()),
2✔
123
        };
2✔
124
        Self(Arc::new(state))
2✔
125
    }
2✔
126

127
    /// Determines if the problem report contains any issues.
128
    ///
129
    /// This method checks whether there are any problems recorded in the report by
130
    /// examining the length of the internal `report` field. If the report is empty,
131
    /// it returns `false`, indicating that there are no problems. Otherwise, it
132
    /// returns `true`.
133
    ///
134
    /// # Returns
135
    /// A boolean value:
136
    /// - `true` if the problem report contains one or more issues.
137
    /// - `false` if the problem report is empty and has no issues.
138
    ///
139
    /// # Examples
140
    /// ```rust
141
    /// # use catalyst_types::problem_report::ProblemReport;
142
    /// let report = ProblemReport::new("Example context");
143
    /// assert_eq!(report.is_problematic(), false); // Initially, there are no problems.
144
    /// ```
145
    #[must_use]
146
    pub fn is_problematic(&self) -> bool {
3✔
147
        !self.0.report.0.is_empty()
3✔
148
    }
3✔
149

150
    /// Add an entry to the report
151
    fn add_entry(&self, kind: Kind, context: &str) {
2✔
152
        self.0.report.0.push(Entry {
2✔
153
            kind,
2✔
154
            context: context.to_owned(),
2✔
155
        });
2✔
156
    }
2✔
157

158
    /// Report that a field was missing in the problem report.
159
    ///
160
    /// This method adds an entry to the problem report indicating that a specified field
161
    /// is absent, along with any additional context provided.
162
    ///
163
    /// # Arguments
164
    ///
165
    /// * `field_name`: A string slice representing the name of the missing field.
166
    /// * `context`: A string slice providing additional context or information about
167
    ///   where and why this field is missing.
168
    ///
169
    /// # Example
170
    ///
171
    /// ```rust
172
    /// # use catalyst_types::problem_report::ProblemReport;
173
    /// // Assuming you have a ProblemReport instance `report`
174
    /// let report = ProblemReport::new("RBAC Registration Decoding");
175
    /// report.missing_field("name", "In the JSON payload for user creation");
176
    /// ```
177
    pub fn missing_field(&self, field_name: &str, context: &str) {
×
178
        self.add_entry(
×
179
            Kind::MissingField {
×
180
                field: field_name.to_owned(),
×
181
            },
×
182
            context,
×
183
        );
184
    }
×
185

186
    /// Reports that an unknown and unexpected field was encountered in the problem
187
    /// report.
188
    ///
189
    /// This method adds an entry to the problem report indicating that a specified field
190
    /// was found but is not recognized or expected, along with its value and any
191
    /// additional context provided.
192
    ///
193
    /// # Arguments
194
    ///
195
    /// * `field_name`: A string slice representing the name of the unknown field.
196
    /// * `value`: A string slice representing the value of the unknown field.
197
    /// * `context`: A string slice providing additional context or information about
198
    ///   where and why this field is unexpected.
199
    ///
200
    /// # Example
201
    ///
202
    /// ```rust
203
    /// # use catalyst_types::problem_report::ProblemReport;
204
    /// // Assuming you have a ProblemReport instance `report`
205
    /// let report = ProblemReport::new("RBAC Registration Decoding");
206
    /// report.unknown_field(
207
    ///     "unsupported_option",
208
    ///     "true",
209
    ///     "In the JSON configuration file",
210
    /// );
211
    /// ```
212
    pub fn unknown_field(&self, field_name: &str, value: &str, context: &str) {
×
213
        self.add_entry(
×
214
            Kind::UnknownField {
×
215
                field: field_name.to_owned(),
×
216
                value: value.to_owned(),
×
217
            },
×
218
            context,
×
219
        );
220
    }
×
221

222
    /// Reports that a field has an invalid value in the problem report.
223
    ///
224
    /// This method adds an entry to the problem report indicating that a specified field
225
    /// contains a value which does not meet the required constraints, along with any
226
    /// additional context provided.
227
    ///
228
    /// # Arguments
229
    ///
230
    /// * `field_name`: A string slice representing the name of the field with the invalid
231
    ///   value.
232
    /// * `found`: A string slice representing the actual value found in the field that is
233
    ///   deemed invalid.
234
    /// * `constraint`: A string slice representing the constraint or expected format for
235
    ///   the field's value.
236
    /// * `context`: A string slice providing additional context or information about
237
    ///   where and why this field has an invalid value.
238
    ///
239
    /// # Example
240
    ///
241
    /// ```rust
242
    /// # use catalyst_types::problem_report::ProblemReport;
243
    /// // Assuming you have a ProblemReport instance `report`
244
    /// let report = ProblemReport::new("RBAC Registration Decoding");
245
    /// report.invalid_value(
246
    ///     "age",
247
    ///     "300",
248
    ///     "must be between 18 and 99",
249
    ///     "During user registration",
250
    /// );
251
    /// ```
252
    pub fn invalid_value(&self, field_name: &str, found: &str, constraint: &str, context: &str) {
1✔
253
        self.add_entry(
1✔
254
            Kind::InvalidValue {
1✔
255
                field: field_name.to_owned(),
1✔
256
                value: found.to_owned(),
1✔
257
                constraint: constraint.to_owned(),
1✔
258
            },
1✔
259
            context,
1✔
260
        );
261
    }
1✔
262

263
    /// Reports that a field has an invalid encoding in the problem report.
264
    ///
265
    /// This method adds an entry to the problem report indicating that a specified field
266
    /// contains data which is encoded using a format that does not match the expected or
267
    /// required encoding, along with any additional context provided.
268
    ///
269
    /// # Arguments
270
    ///
271
    /// * `field_name`: A string slice representing the name of the field with the invalid
272
    ///   encoding.
273
    /// * `detected_encoding`: A string slice representing the detected encoding of the
274
    ///   data in the field.
275
    /// * `expected_encoding`: A string slice representing the expected or required
276
    ///   encoding for the field's data.
277
    /// * `context`: A string slice providing additional context or information about
278
    ///   where and why this field has an invalid encoding.
279
    ///
280
    /// # Example
281
    ///
282
    /// ```rust
283
    /// # use catalyst_types::problem_report::ProblemReport;
284
    /// // Assuming you have a ProblemReport instance `report`
285
    /// let report = ProblemReport::new("RBAC Registration Decoding");
286
    /// report.invalid_encoding("data", "UTF-8", "ASCII", "During data import");
287
    /// ```
288
    pub fn invalid_encoding(
×
289
        &self, field_name: &str, detected_encoding: &str, expected_encoding: &str, context: &str,
×
290
    ) {
×
291
        self.add_entry(
×
292
            Kind::InvalidEncoding {
×
293
                field: field_name.to_owned(),
×
294
                encoded: detected_encoding.to_owned(),
×
295
                expected: expected_encoding.to_owned(),
×
296
            },
×
297
            context,
×
298
        );
299
    }
×
300

301
    /// Reports an invalid validation or cross-field validation error in the problem
302
    /// report.
303
    ///
304
    /// This method adds an entry to the problem report indicating that there is a
305
    /// functional validation issue, typically involving multiple fields or data points
306
    /// not meeting specific validation criteria, along with any additional context
307
    /// provided.
308
    ///
309
    /// # Arguments
310
    ///
311
    /// * `explanation`: A string slice providing a detailed explanation of why the
312
    ///   validation failed.
313
    /// * `context`: A string slice providing additional context or information about
314
    ///   where and why this functional validation error occurred.
315
    ///
316
    /// # Example
317
    ///
318
    /// ```rust
319
    /// # use catalyst_types::problem_report::ProblemReport;
320
    /// // Assuming you have a ProblemReport instance `report`
321
    /// let report = ProblemReport::new("RBAC Registration Decoding");
322
    /// report.functional_validation(
323
    ///     "End date cannot be before start date",
324
    ///     "During contract creation",
325
    /// );
326
    /// ```
327
    pub fn functional_validation(&self, explanation: &str, context: &str) {
×
328
        self.add_entry(
×
329
            Kind::FunctionalValidation {
×
330
                explanation: explanation.to_owned(),
×
331
            },
×
332
            context,
×
333
        );
334
    }
×
335

336
    /// Reports that duplicate field was detected in the problem report.
337
    ///
338
    /// This method adds an entry to the problem report indicating that duplicate field
339
    /// is found, along with the description of the duplicate field and any additional
340
    /// context.
341
    ///
342
    /// # Arguments
343
    ///
344
    /// * `field`: A string slice representing the value of the duplicate field.
345
    /// * `description`: An additional information about the duplicate field.
346
    /// * `context`: A string slice providing additional context or information about
347
    ///   where and why this duplicate field was detected.
348
    ///
349
    /// # Example
350
    ///
351
    /// ```rust
352
    /// # use catalyst_types::problem_report::ProblemReport;
353
    /// let report = ProblemReport::new("RBAC Registration Decoding");
354
    /// report.duplicate_field(
355
    ///     "key 0",
356
    ///     "key is already defined, redundant key found in item 6 in RBAC map",
357
    ///     "RBAC purpose",
358
    /// );
359
    /// ```
360
    pub fn duplicate_field(&self, field: &str, description: &str, context: &str) {
×
361
        self.add_entry(
×
362
            Kind::DuplicateField {
×
363
                field: field.to_owned(),
×
364
                description: description.to_owned(),
×
365
            },
×
366
            context,
×
367
        );
368
    }
×
369

370
    /// Reports a conversion error.
371
    ///
372
    /// This method adds an entry to the problem report indicating that a field failed to
373
    /// convert to the expected type, along with the value that failed to convert and
374
    /// the expected type.
375
    ///
376
    /// # Arguments
377
    ///
378
    /// * `field`: A string slice representing the field that failed to convert.
379
    /// * `value`: A string slice representing the value that failed to convert.
380
    /// * `expected_type`: A string slice representing the type that the value was
381
    ///   expected to convert to.
382
    ///
383
    /// # Example
384
    ///
385
    /// ```rust
386
    /// use catalyst_types::problem_report::ProblemReport;
387
    /// let report = ProblemReport::new("RBAC Registration Decoding");
388
    /// report.conversion_error(
389
    ///     "address bytes",
390
    ///     "[1, 2, 3, 4]",
391
    ///     "Address",
392
    ///     "RBAC stake address",
393
    /// );
394
    /// ```
395
    pub fn conversion_error(&self, field: &str, value: &str, expected_type: &str, context: &str) {
×
396
        self.add_entry(
×
397
            Kind::ConversionError {
×
398
                field: field.to_owned(),
×
399
                value: value.to_owned(),
×
400
                expected_type: expected_type.to_owned(),
×
401
            },
×
402
            context,
×
403
        );
404
    }
×
405

406
    /// Reports an uncategorized problem with the given description and context.
407
    ///
408
    /// This method is intended for use in rare situations where a specific type of
409
    /// problem has not been identified or defined. Using this method frequently can
410
    /// lead to disorganized reporting and difficulty in analyzing problems. For
411
    /// better clarity and organization, consider creating more distinct categories of
412
    /// problems to report using methods that specifically handle those types (e.g.,
413
    /// `other_problem`, `technical_issue`, etc.).
414
    ///
415
    /// # Parameters:
416
    /// - `description`: A brief description of the problem. This should clearly convey
417
    ///   what went wrong or what caused the issue.
418
    /// - `context`: Additional information that might help in understanding the context
419
    ///   or environment where the problem occurred. This could include details about the
420
    ///   system, user actions, or any other relevant data.
421
    ///
422
    /// # Example
423
    ///
424
    /// ```rust
425
    /// # use catalyst_types::problem_report::ProblemReport;
426
    /// // Assuming you have a ProblemReport instance `report`
427
    /// let report = ProblemReport::new("RBAC Registration Decoding");
428
    /// report.other(
429
    ///     "Some other problem happened, but its rare, otherwise we would have a defined problem type.",
430
    ///     "During contract creation",
431
    /// );
432
    /// ```
433
    pub fn other(&self, description: &str, context: &str) {
1✔
434
        self.add_entry(
1✔
435
            Kind::Other {
1✔
436
                description: description.to_owned(),
1✔
437
            },
1✔
438
            context,
1✔
439
        );
440
    }
1✔
441
}
442

443
#[cfg(test)]
444
mod tests {
445
    use super::*;
446

447
    // Check that the Clone implementation performs the shallow copy, so all instances share
448
    // the same state.
449
    #[test]
450
    fn clone_shared_state() {
1✔
451
        let original = ProblemReport::new("top level context");
1✔
452
        assert!(!original.is_problematic());
1✔
453

454
        let clone = original.clone();
1✔
455
        clone.other("description", "error context");
1✔
456
        assert!(clone.is_problematic());
1✔
457

458
        // The original report must have the same (problematic) state.
459
        assert!(original.is_problematic());
1✔
460
    }
1✔
461

462
    #[test]
463
    fn serialize() {
1✔
464
        let report = ProblemReport::new("top level context");
1✔
465
        report.invalid_value("field name", "found", "constraint", "context");
1✔
466

467
        let serialized = serde_json::to_string(&report).unwrap();
1✔
468
        let expected = r#"{"context":"top level context","report":[{"kind":{"type":"InvalidValue","field":"field name","value":"found","constraint":"constraint"},"context":"context"}]}"#;
1✔
469
        assert_eq!(serialized, expected);
1✔
470
    }
1✔
471
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc