Skip to main content

qubit_validator/report/
failure_id.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8
9//! Opaque identities for failures retained by one validation report.
10
11use std::sync::atomic::AtomicU64;
12use std::sync::atomic::Ordering;
13
14/// Identifies one retained failure within its originating report.
15///
16/// IDs are issued by [`super::ValidationReport::record_outcome`]. A caller
17/// cannot construct an ID, and a report rejects IDs issued by another report.
18///
19/// # Examples
20///
21/// ```
22/// use qubit_validator::FailureId;
23/// use qubit_validator::ValidationOutcome;
24/// use qubit_validator::ValidationPath;
25/// use qubit_validator::ValidationReport;
26/// use qubit_validator::ValidatorId;
27/// use qubit_validator::Violation;
28/// use qubit_validator::ViolationCode;
29///
30/// let mut report = ValidationReport::new();
31/// let outcome = report.record_outcome(
32///     0,
33///     ValidationPath::root(),
34///     ValidationOutcome::invalid(vec![Violation::new(
35///         ValidatorId::new("text.required"),
36///         ViolationCode::new("text.blank"),
37///     )])?,
38/// )?;
39/// let failure: FailureId = outcome.failure_ids()[0];
40/// assert!(report.failure(failure).is_some());
41/// # Ok::<(), Box<dyn std::error::Error>>(())
42/// ```
43#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
44pub struct FailureId {
45    /// Process-local identity of the report that issued this failure ID.
46    pub(crate) report: u64,
47    /// Zero-based position of the retained violation in that report.
48    pub(crate) index: usize,
49}
50
51impl FailureId {
52    /// Creates an ID for a retained failure at `index` in `report`.
53    ///
54    /// The caller must ensure that the report identity and index refer to an
55    /// entry already retained by the originating report.
56    #[must_use]
57    #[inline]
58    pub(crate) const fn new(report: u64, index: usize) -> Self {
59        Self { report, index }
60    }
61}
62
63/// Allocates a unique process-local identity for a new report.
64///
65/// # Panics
66///
67/// Panics when assigning the next identity would overflow `u64`, after
68/// `u64::MAX - 1` report identities have been issued.
69#[must_use]
70pub(crate) fn next_report_id() -> u64 {
71    static NEXT_REPORT_ID: AtomicU64 = AtomicU64::new(1);
72    NEXT_REPORT_ID
73        .fetch_update(Ordering::Relaxed, Ordering::Relaxed, |current| current.checked_add(1))
74        .expect("validation report identity space exhausted")
75}