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

input-output-hk / catalyst-libs / 17621772976

10 Sep 2025 05:28PM UTC coverage: 68.54% (-0.1%) from 68.668%
17621772976

Pull #549

github

web-flow
Merge e869dd8ed into 9b04d25f3
Pull Request #549: feat(rust/catalyst-types): Add getters to `ProblemReport` and its `Entry`s

0 of 12 new or added lines in 1 file covered. (0.0%)

15 existing lines in 1 file now uncovered.

13612 of 19860 relevant lines covered (68.54%)

2929.05 hits per line

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

27.08
/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::{ConcurrentElement, ConcurrentVec};
10

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

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

82
impl Entry {
83
    /// Gets the kind of the problem of the entry.
84
    #[must_use]
NEW
85
    pub fn kind(&self) -> &Kind {
×
NEW
86
        &self.kind
×
NEW
87
    }
×
88

89
    /// Gets extra information of the entry.
90
    #[must_use]
NEW
91
    pub fn context(&self) -> &String {
×
NEW
92
        &self.context
×
NEW
93
    }
×
94
}
95

96
/// The Problem Report list
97
#[derive(Debug, Clone)]
98
pub struct Report(ConcurrentVec<Entry>);
99

100
/// An inner state of the report.
101
#[derive(Debug)]
102
pub struct State {
103
    /// What context does the whole report have
104
    context: String,
105
    /// The report itself
106
    report: Report,
107
}
108

109
/// Problem Report.
110
///
111
/// This structure allows making a cheap copies that share the same state.
112
#[derive(Debug, Clone)]
113
pub struct ProblemReport(Arc<State>);
114

115
impl ProblemReport {
116
    /// Creates a new `ProblemReport` with the given context string.
117
    ///
118
    /// # Arguments
119
    /// * `context`: A reference to a string slice that is used as the context for the
120
    ///   problem report.
121
    ///
122
    /// # Returns
123
    /// A new instance of `ProblemReport`.
124
    ///
125
    /// # Examples
126
    /// ```rust
127
    /// # use catalyst_types::problem_report::ProblemReport;
128
    /// let report = ProblemReport::new("RBAC Registration Decoding");
129
    /// ```
130
    #[must_use]
131
    pub fn new(context: &str) -> Self {
1✔
132
        let state = State {
1✔
133
            context: context.to_owned(),
1✔
134
            report: Report(ConcurrentVec::new()),
1✔
135
        };
1✔
136
        Self(Arc::new(state))
1✔
137
    }
1✔
138

139
    /// Determines if the problem report contains any issues.
140
    ///
141
    /// This method checks whether there are any problems recorded in the report by
142
    /// examining the length of the internal `report` field. If the report is empty,
143
    /// it returns `false`, indicating that there are no problems. Otherwise, it
144
    /// returns `true`.
145
    ///
146
    /// # Returns
147
    /// A boolean value:
148
    /// - `true` if the problem report contains one or more issues.
149
    /// - `false` if the problem report is empty and has no issues.
150
    ///
151
    /// # Examples
152
    /// ```rust
153
    /// # use catalyst_types::problem_report::ProblemReport;
154
    /// let report = ProblemReport::new("Example context");
155
    /// assert_eq!(report.is_problematic(), false); // Initially, there are no problems.
156
    /// ```
157
    #[must_use]
158
    pub fn is_problematic(&self) -> bool {
3✔
159
        !self.0.report.0.is_empty()
3✔
160
    }
3✔
161

162
    /// Gets entries from the report.
NEW
163
    pub fn entries(&self) -> impl Iterator<Item = &ConcurrentElement<Entry>> {
×
NEW
164
        self.0.report.0.iter()
×
NEW
165
    }
×
166

167
    /// Gets context from the report.
168
    #[must_use]
NEW
169
    pub fn context(&self) -> &String {
×
NEW
170
        &self.0.context
×
NEW
171
    }
×
172

173
    /// Add an entry to the report
174
    fn add_entry(
1✔
175
        &self,
1✔
176
        kind: Kind,
1✔
177
        context: &str,
1✔
178
    ) {
1✔
179
        self.0.report.0.push(Entry {
1✔
180
            kind,
1✔
181
            context: context.to_owned(),
1✔
182
        });
1✔
183
    }
1✔
184

185
    /// Report that a field was missing in the problem report.
186
    ///
187
    /// This method adds an entry to the problem report indicating that a specified field
188
    /// is absent, along with any additional context provided.
189
    ///
190
    /// # Arguments
191
    ///
192
    /// * `field_name`: A string slice representing the name of the missing field.
193
    /// * `context`: A string slice providing additional context or information about
194
    ///   where and why this field is missing.
195
    ///
196
    /// # Example
197
    ///
198
    /// ```rust
199
    /// # use catalyst_types::problem_report::ProblemReport;
200
    /// // Assuming you have a ProblemReport instance `report`
201
    /// let report = ProblemReport::new("RBAC Registration Decoding");
202
    /// report.missing_field("name", "In the JSON payload for user creation");
203
    /// ```
204
    pub fn missing_field(
×
205
        &self,
×
206
        field_name: &str,
×
207
        context: &str,
×
208
    ) {
×
209
        self.add_entry(
×
210
            Kind::MissingField {
×
211
                field: field_name.to_owned(),
×
212
            },
×
213
            context,
×
214
        );
215
    }
×
216

217
    /// Reports that an unknown and unexpected field was encountered in the problem
218
    /// report.
219
    ///
220
    /// This method adds an entry to the problem report indicating that a specified field
221
    /// was found but is not recognized or expected, along with its value and any
222
    /// additional context provided.
223
    ///
224
    /// # Arguments
225
    ///
226
    /// * `field_name`: A string slice representing the name of the unknown field.
227
    /// * `value`: A string slice representing the value of the unknown field.
228
    /// * `context`: A string slice providing additional context or information about
229
    ///   where and why this field is unexpected.
230
    ///
231
    /// # Example
232
    ///
233
    /// ```rust
234
    /// # use catalyst_types::problem_report::ProblemReport;
235
    /// // Assuming you have a ProblemReport instance `report`
236
    /// let report = ProblemReport::new("RBAC Registration Decoding");
237
    /// report.unknown_field(
238
    ///     "unsupported_option",
239
    ///     "true",
240
    ///     "In the JSON configuration file",
241
    /// );
242
    /// ```
243
    pub fn unknown_field(
×
244
        &self,
×
245
        field_name: &str,
×
246
        value: &str,
×
247
        context: &str,
×
248
    ) {
×
249
        self.add_entry(
×
250
            Kind::UnknownField {
×
251
                field: field_name.to_owned(),
×
252
                value: value.to_owned(),
×
253
            },
×
254
            context,
×
255
        );
256
    }
×
257

258
    /// Reports that a field has an invalid value in the problem report.
259
    ///
260
    /// This method adds an entry to the problem report indicating that a specified field
261
    /// contains a value which does not meet the required constraints, along with any
262
    /// additional context provided.
263
    ///
264
    /// # Arguments
265
    ///
266
    /// * `field_name`: A string slice representing the name of the field with the invalid
267
    ///   value.
268
    /// * `found`: A string slice representing the actual value found in the field that is
269
    ///   deemed invalid.
270
    /// * `constraint`: A string slice representing the constraint or expected format for
271
    ///   the field's value.
272
    /// * `context`: A string slice providing additional context or information about
273
    ///   where and why this field has an invalid value.
274
    ///
275
    /// # Example
276
    ///
277
    /// ```rust
278
    /// # use catalyst_types::problem_report::ProblemReport;
279
    /// // Assuming you have a ProblemReport instance `report`
280
    /// let report = ProblemReport::new("RBAC Registration Decoding");
281
    /// report.invalid_value(
282
    ///     "age",
283
    ///     "300",
284
    ///     "must be between 18 and 99",
285
    ///     "During user registration",
286
    /// );
287
    /// ```
UNCOV
288
    pub fn invalid_value(
×
UNCOV
289
        &self,
×
UNCOV
290
        field_name: &str,
×
UNCOV
291
        found: &str,
×
UNCOV
292
        constraint: &str,
×
UNCOV
293
        context: &str,
×
UNCOV
294
    ) {
×
UNCOV
295
        self.add_entry(
×
UNCOV
296
            Kind::InvalidValue {
×
UNCOV
297
                field: field_name.to_owned(),
×
UNCOV
298
                value: found.to_owned(),
×
UNCOV
299
                constraint: constraint.to_owned(),
×
UNCOV
300
            },
×
UNCOV
301
            context,
×
302
        );
UNCOV
303
    }
×
304

305
    /// Reports that a field has an invalid encoding in the problem report.
306
    ///
307
    /// This method adds an entry to the problem report indicating that a specified field
308
    /// contains data which is encoded using a format that does not match the expected or
309
    /// required encoding, along with any additional context provided.
310
    ///
311
    /// # Arguments
312
    ///
313
    /// * `field_name`: A string slice representing the name of the field with the invalid
314
    ///   encoding.
315
    /// * `detected_encoding`: A string slice representing the detected encoding of the
316
    ///   data in the field.
317
    /// * `expected_encoding`: A string slice representing the expected or required
318
    ///   encoding for the field's data.
319
    /// * `context`: A string slice providing additional context or information about
320
    ///   where and why this field has an invalid encoding.
321
    ///
322
    /// # Example
323
    ///
324
    /// ```rust
325
    /// # use catalyst_types::problem_report::ProblemReport;
326
    /// // Assuming you have a ProblemReport instance `report`
327
    /// let report = ProblemReport::new("RBAC Registration Decoding");
328
    /// report.invalid_encoding("data", "UTF-8", "ASCII", "During data import");
329
    /// ```
330
    pub fn invalid_encoding(
×
331
        &self,
×
332
        field_name: &str,
×
333
        detected_encoding: &str,
×
334
        expected_encoding: &str,
×
335
        context: &str,
×
336
    ) {
×
337
        self.add_entry(
×
338
            Kind::InvalidEncoding {
×
339
                field: field_name.to_owned(),
×
340
                encoded: detected_encoding.to_owned(),
×
341
                expected: expected_encoding.to_owned(),
×
342
            },
×
343
            context,
×
344
        );
345
    }
×
346

347
    /// Reports an invalid validation or cross-field validation error in the problem
348
    /// report.
349
    ///
350
    /// This method adds an entry to the problem report indicating that there is a
351
    /// functional validation issue, typically involving multiple fields or data points
352
    /// not meeting specific validation criteria, along with any additional context
353
    /// provided.
354
    ///
355
    /// # Arguments
356
    ///
357
    /// * `explanation`: A string slice providing a detailed explanation of why the
358
    ///   validation failed.
359
    /// * `context`: A string slice providing additional context or information about
360
    ///   where and why this functional validation error occurred.
361
    ///
362
    /// # Example
363
    ///
364
    /// ```rust
365
    /// # use catalyst_types::problem_report::ProblemReport;
366
    /// // Assuming you have a ProblemReport instance `report`
367
    /// let report = ProblemReport::new("RBAC Registration Decoding");
368
    /// report.functional_validation(
369
    ///     "End date cannot be before start date",
370
    ///     "During contract creation",
371
    /// );
372
    /// ```
373
    pub fn functional_validation(
×
374
        &self,
×
375
        explanation: &str,
×
376
        context: &str,
×
377
    ) {
×
378
        self.add_entry(
×
379
            Kind::FunctionalValidation {
×
380
                explanation: explanation.to_owned(),
×
381
            },
×
382
            context,
×
383
        );
384
    }
×
385

386
    /// Reports that duplicate field was detected in the problem report.
387
    ///
388
    /// This method adds an entry to the problem report indicating that duplicate field
389
    /// is found, along with the description of the duplicate field and any additional
390
    /// context.
391
    ///
392
    /// # Arguments
393
    ///
394
    /// * `field`: A string slice representing the value of the duplicate field.
395
    /// * `description`: An additional information about the duplicate field.
396
    /// * `context`: A string slice providing additional context or information about
397
    ///   where and why this duplicate field was detected.
398
    ///
399
    /// # Example
400
    ///
401
    /// ```rust
402
    /// # use catalyst_types::problem_report::ProblemReport;
403
    /// let report = ProblemReport::new("RBAC Registration Decoding");
404
    /// report.duplicate_field(
405
    ///     "key 0",
406
    ///     "key is already defined, redundant key found in item 6 in RBAC map",
407
    ///     "RBAC purpose",
408
    /// );
409
    /// ```
410
    pub fn duplicate_field(
×
411
        &self,
×
412
        field: &str,
×
413
        description: &str,
×
414
        context: &str,
×
415
    ) {
×
416
        self.add_entry(
×
417
            Kind::DuplicateField {
×
418
                field: field.to_owned(),
×
419
                description: description.to_owned(),
×
420
            },
×
421
            context,
×
422
        );
423
    }
×
424

425
    /// Reports a conversion error.
426
    ///
427
    /// This method adds an entry to the problem report indicating that a field failed to
428
    /// convert to the expected type, along with the value that failed to convert and
429
    /// the expected type.
430
    ///
431
    /// # Arguments
432
    ///
433
    /// * `field`: A string slice representing the field that failed to convert.
434
    /// * `value`: A string slice representing the value that failed to convert.
435
    /// * `expected_type`: A string slice representing the type that the value was
436
    ///   expected to convert to.
437
    ///
438
    /// # Example
439
    ///
440
    /// ```rust
441
    /// use catalyst_types::problem_report::ProblemReport;
442
    /// let report = ProblemReport::new("RBAC Registration Decoding");
443
    /// report.conversion_error(
444
    ///     "address bytes",
445
    ///     "[1, 2, 3, 4]",
446
    ///     "Address",
447
    ///     "RBAC stake address",
448
    /// );
449
    /// ```
450
    pub fn conversion_error(
×
451
        &self,
×
452
        field: &str,
×
453
        value: &str,
×
454
        expected_type: &str,
×
455
        context: &str,
×
456
    ) {
×
457
        self.add_entry(
×
458
            Kind::ConversionError {
×
459
                field: field.to_owned(),
×
460
                value: value.to_owned(),
×
461
                expected_type: expected_type.to_owned(),
×
462
            },
×
463
            context,
×
464
        );
465
    }
×
466

467
    /// Reports an uncategorized problem with the given description and context.
468
    ///
469
    /// This method is intended for use in rare situations where a specific type of
470
    /// problem has not been identified or defined. Using this method frequently can
471
    /// lead to disorganized reporting and difficulty in analyzing problems. For
472
    /// better clarity and organization, consider creating more distinct categories of
473
    /// problems to report using methods that specifically handle those types (e.g.,
474
    /// `other_problem`, `technical_issue`, etc.).
475
    ///
476
    /// # Parameters:
477
    /// - `description`: A brief description of the problem. This should clearly convey
478
    ///   what went wrong or what caused the issue.
479
    /// - `context`: Additional information that might help in understanding the context
480
    ///   or environment where the problem occurred. This could include details about the
481
    ///   system, user actions, or any other relevant data.
482
    ///
483
    /// # Example
484
    ///
485
    /// ```rust
486
    /// # use catalyst_types::problem_report::ProblemReport;
487
    /// // Assuming you have a ProblemReport instance `report`
488
    /// let report = ProblemReport::new("RBAC Registration Decoding");
489
    /// report.other(
490
    ///     "Some other problem happened, but its rare, otherwise we would have a defined problem type.",
491
    ///     "During contract creation",
492
    /// );
493
    /// ```
494
    pub fn other(
1✔
495
        &self,
1✔
496
        description: &str,
1✔
497
        context: &str,
1✔
498
    ) {
1✔
499
        self.add_entry(
1✔
500
            Kind::Other {
1✔
501
                description: description.to_owned(),
1✔
502
            },
1✔
503
            context,
1✔
504
        );
505
    }
1✔
506
}
507

508
#[cfg(test)]
509
mod tests {
510
    use super::*;
511

512
    // Check that the Clone implementation performs the shallow copy, so all instances share
513
    // the same state.
514
    #[test]
515
    fn clone_shared_state() {
1✔
516
        let original = ProblemReport::new("top level context");
1✔
517
        assert!(!original.is_problematic());
1✔
518

519
        let clone = original.clone();
1✔
520
        clone.other("description", "error context");
1✔
521
        assert!(clone.is_problematic());
1✔
522

523
        // The original report must have the same (problematic) state.
524
        assert!(original.is_problematic());
1✔
525
    }
1✔
526
}
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