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

input-output-hk / catalyst-libs / 16758889759

05 Aug 2025 07:03PM UTC coverage: 65.822% (-0.2%) from 66.061%
16758889759

push

github

web-flow
feat(general): new rustfmt rules (#461)

* bump rust-ci

* update rustfmt.toml

* apply fmt

1488 of 2319 new or added lines in 158 files covered. (64.17%)

23 existing lines in 14 files now uncovered.

12150 of 18459 relevant lines covered (65.82%)

3171.34 hits per line

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

43.88
/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(
2✔
152
        &self,
2✔
153
        kind: Kind,
2✔
154
        context: &str,
2✔
155
    ) {
2✔
156
        self.0.report.0.push(Entry {
2✔
157
            kind,
2✔
158
            context: context.to_owned(),
2✔
159
        });
2✔
160
    }
2✔
161

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

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

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

282
    /// Reports that a field has an invalid encoding in the problem report.
283
    ///
284
    /// This method adds an entry to the problem report indicating that a specified field
285
    /// contains data which is encoded using a format that does not match the expected or
286
    /// required encoding, along with any additional context provided.
287
    ///
288
    /// # Arguments
289
    ///
290
    /// * `field_name`: A string slice representing the name of the field with the invalid
291
    ///   encoding.
292
    /// * `detected_encoding`: A string slice representing the detected encoding of the
293
    ///   data in the field.
294
    /// * `expected_encoding`: A string slice representing the expected or required
295
    ///   encoding for the field's data.
296
    /// * `context`: A string slice providing additional context or information about
297
    ///   where and why this field has an invalid encoding.
298
    ///
299
    /// # Example
300
    ///
301
    /// ```rust
302
    /// # use catalyst_types::problem_report::ProblemReport;
303
    /// // Assuming you have a ProblemReport instance `report`
304
    /// let report = ProblemReport::new("RBAC Registration Decoding");
305
    /// report.invalid_encoding("data", "UTF-8", "ASCII", "During data import");
306
    /// ```
307
    pub fn invalid_encoding(
×
NEW
308
        &self,
×
NEW
309
        field_name: &str,
×
NEW
310
        detected_encoding: &str,
×
NEW
311
        expected_encoding: &str,
×
NEW
312
        context: &str,
×
313
    ) {
×
314
        self.add_entry(
×
315
            Kind::InvalidEncoding {
×
316
                field: field_name.to_owned(),
×
317
                encoded: detected_encoding.to_owned(),
×
318
                expected: expected_encoding.to_owned(),
×
319
            },
×
320
            context,
×
321
        );
322
    }
×
323

324
    /// Reports an invalid validation or cross-field validation error in the problem
325
    /// report.
326
    ///
327
    /// This method adds an entry to the problem report indicating that there is a
328
    /// functional validation issue, typically involving multiple fields or data points
329
    /// not meeting specific validation criteria, along with any additional context
330
    /// provided.
331
    ///
332
    /// # Arguments
333
    ///
334
    /// * `explanation`: A string slice providing a detailed explanation of why the
335
    ///   validation failed.
336
    /// * `context`: A string slice providing additional context or information about
337
    ///   where and why this functional validation error occurred.
338
    ///
339
    /// # Example
340
    ///
341
    /// ```rust
342
    /// # use catalyst_types::problem_report::ProblemReport;
343
    /// // Assuming you have a ProblemReport instance `report`
344
    /// let report = ProblemReport::new("RBAC Registration Decoding");
345
    /// report.functional_validation(
346
    ///     "End date cannot be before start date",
347
    ///     "During contract creation",
348
    /// );
349
    /// ```
NEW
350
    pub fn functional_validation(
×
NEW
351
        &self,
×
NEW
352
        explanation: &str,
×
NEW
353
        context: &str,
×
NEW
354
    ) {
×
355
        self.add_entry(
×
356
            Kind::FunctionalValidation {
×
357
                explanation: explanation.to_owned(),
×
358
            },
×
359
            context,
×
360
        );
361
    }
×
362

363
    /// Reports that duplicate field was detected in the problem report.
364
    ///
365
    /// This method adds an entry to the problem report indicating that duplicate field
366
    /// is found, along with the description of the duplicate field and any additional
367
    /// context.
368
    ///
369
    /// # Arguments
370
    ///
371
    /// * `field`: A string slice representing the value of the duplicate field.
372
    /// * `description`: An additional information about the duplicate field.
373
    /// * `context`: A string slice providing additional context or information about
374
    ///   where and why this duplicate field was detected.
375
    ///
376
    /// # Example
377
    ///
378
    /// ```rust
379
    /// # use catalyst_types::problem_report::ProblemReport;
380
    /// let report = ProblemReport::new("RBAC Registration Decoding");
381
    /// report.duplicate_field(
382
    ///     "key 0",
383
    ///     "key is already defined, redundant key found in item 6 in RBAC map",
384
    ///     "RBAC purpose",
385
    /// );
386
    /// ```
NEW
387
    pub fn duplicate_field(
×
NEW
388
        &self,
×
NEW
389
        field: &str,
×
NEW
390
        description: &str,
×
NEW
391
        context: &str,
×
NEW
392
    ) {
×
393
        self.add_entry(
×
394
            Kind::DuplicateField {
×
395
                field: field.to_owned(),
×
396
                description: description.to_owned(),
×
397
            },
×
398
            context,
×
399
        );
400
    }
×
401

402
    /// Reports a conversion error.
403
    ///
404
    /// This method adds an entry to the problem report indicating that a field failed to
405
    /// convert to the expected type, along with the value that failed to convert and
406
    /// the expected type.
407
    ///
408
    /// # Arguments
409
    ///
410
    /// * `field`: A string slice representing the field that failed to convert.
411
    /// * `value`: A string slice representing the value that failed to convert.
412
    /// * `expected_type`: A string slice representing the type that the value was
413
    ///   expected to convert to.
414
    ///
415
    /// # Example
416
    ///
417
    /// ```rust
418
    /// use catalyst_types::problem_report::ProblemReport;
419
    /// let report = ProblemReport::new("RBAC Registration Decoding");
420
    /// report.conversion_error(
421
    ///     "address bytes",
422
    ///     "[1, 2, 3, 4]",
423
    ///     "Address",
424
    ///     "RBAC stake address",
425
    /// );
426
    /// ```
NEW
427
    pub fn conversion_error(
×
NEW
428
        &self,
×
NEW
429
        field: &str,
×
NEW
430
        value: &str,
×
NEW
431
        expected_type: &str,
×
NEW
432
        context: &str,
×
NEW
433
    ) {
×
434
        self.add_entry(
×
435
            Kind::ConversionError {
×
436
                field: field.to_owned(),
×
437
                value: value.to_owned(),
×
438
                expected_type: expected_type.to_owned(),
×
439
            },
×
440
            context,
×
441
        );
442
    }
×
443

444
    /// Reports an uncategorized problem with the given description and context.
445
    ///
446
    /// This method is intended for use in rare situations where a specific type of
447
    /// problem has not been identified or defined. Using this method frequently can
448
    /// lead to disorganized reporting and difficulty in analyzing problems. For
449
    /// better clarity and organization, consider creating more distinct categories of
450
    /// problems to report using methods that specifically handle those types (e.g.,
451
    /// `other_problem`, `technical_issue`, etc.).
452
    ///
453
    /// # Parameters:
454
    /// - `description`: A brief description of the problem. This should clearly convey
455
    ///   what went wrong or what caused the issue.
456
    /// - `context`: Additional information that might help in understanding the context
457
    ///   or environment where the problem occurred. This could include details about the
458
    ///   system, user actions, or any other relevant data.
459
    ///
460
    /// # Example
461
    ///
462
    /// ```rust
463
    /// # use catalyst_types::problem_report::ProblemReport;
464
    /// // Assuming you have a ProblemReport instance `report`
465
    /// let report = ProblemReport::new("RBAC Registration Decoding");
466
    /// report.other(
467
    ///     "Some other problem happened, but its rare, otherwise we would have a defined problem type.",
468
    ///     "During contract creation",
469
    /// );
470
    /// ```
471
    pub fn other(
1✔
472
        &self,
1✔
473
        description: &str,
1✔
474
        context: &str,
1✔
475
    ) {
1✔
476
        self.add_entry(
1✔
477
            Kind::Other {
1✔
478
                description: description.to_owned(),
1✔
479
            },
1✔
480
            context,
1✔
481
        );
482
    }
1✔
483
}
484

485
#[cfg(test)]
486
mod tests {
487
    use super::*;
488

489
    // Check that the Clone implementation performs the shallow copy, so all instances share
490
    // the same state.
491
    #[test]
492
    fn clone_shared_state() {
1✔
493
        let original = ProblemReport::new("top level context");
1✔
494
        assert!(!original.is_problematic());
1✔
495

496
        let clone = original.clone();
1✔
497
        clone.other("description", "error context");
1✔
498
        assert!(clone.is_problematic());
1✔
499

500
        // The original report must have the same (problematic) state.
501
        assert!(original.is_problematic());
1✔
502
    }
1✔
503

504
    #[test]
505
    fn serialize() {
1✔
506
        let report = ProblemReport::new("top level context");
1✔
507
        report.invalid_value("field name", "found", "constraint", "context");
1✔
508

509
        let serialized = serde_json::to_string(&report).unwrap();
1✔
510
        let expected = r#"{"context":"top level context","report":[{"kind":{"type":"InvalidValue","field":"field name","value":"found","constraint":"constraint"},"context":"context"}]}"#;
1✔
511
        assert_eq!(serialized, expected);
1✔
512
    }
1✔
513
}
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