Skip to main content

qubit_validator/binding/
prepared_outcome.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//! Type-erased results produced by prepared validators.
10
11use super::ViolationDraft;
12use crate::ValidationOutcome;
13use crate::ValidationOutcomeError;
14use crate::ValidatorId;
15use crate::Violation;
16/// The type-erased result produced before rule identity is attached.
17///
18/// # Examples
19///
20/// ```
21/// use qubit_validator::{PreparedOutcome, ViolationCode, ViolationDraft};
22///
23/// let accepted = PreparedOutcome::valid();
24/// let rejected = PreparedOutcome::invalid(vec![
25///     ViolationDraft::new(ViolationCode::new("text.blank")),
26/// ])?;
27/// assert!(matches!(accepted, PreparedOutcome::Valid));
28/// assert!(matches!(rejected, PreparedOutcome::Invalid(drafts) if drafts.len() == 1));
29/// # Ok::<(), qubit_validator::ValidationOutcomeError>(())
30/// ```
31///
32/// The caller's orchestration policy records missing inputs and failed
33/// prerequisites as skipped [`ValidationOutcome`] values.
34#[must_use]
35#[derive(Debug, PartialEq, Eq)]
36#[non_exhaustive]
37pub enum PreparedOutcome {
38    /// Input is valid.
39    Valid,
40    /// Input is invalid with draft violations.
41    Invalid(
42        /// One or more safe draft violations produced by the validator.
43        Vec<ViolationDraft>,
44    ),
45}
46
47impl PreparedOutcome {
48    /// Creates a successful prepared outcome.
49    ///
50    /// # Returns
51    ///
52    /// A prepared outcome representing valid input.
53    #[inline]
54    pub const fn valid() -> Self {
55        Self::Valid
56    }
57
58    /// Creates an invalid prepared outcome with one or more violation drafts.
59    ///
60    /// # Parameters
61    ///
62    /// - `violations`: Draft violations to include in the outcome.
63    ///
64    /// # Returns
65    ///
66    /// The invalid prepared outcome containing `violations`.
67    ///
68    /// # Errors
69    ///
70    /// Returns [`ValidationOutcomeError::EmptyViolations`] when `violations` is
71    /// empty.
72    pub fn invalid(violations: Vec<ViolationDraft>) -> Result<Self, ValidationOutcomeError> {
73        if violations.is_empty() {
74            return Err(ValidationOutcomeError::EmptyViolations);
75        }
76        Ok(Self::Invalid(violations))
77    }
78
79    /// Attaches a bound rule identity to draft violations.
80    ///
81    /// # Parameters
82    ///
83    /// - `rule_id`: Identity of the rule that produced these violations.
84    ///
85    /// # Returns
86    ///
87    /// The corresponding bound validation outcome.
88    ///
89    /// # Errors
90    ///
91    /// Returns [`ValidationOutcomeError::EmptyViolations`] when an invalid
92    /// result has no drafts.
93    pub fn into_bound(self, rule_id: ValidatorId) -> Result<ValidationOutcome, ValidationOutcomeError> {
94        match self {
95            Self::Valid => Ok(ValidationOutcome::Valid),
96            Self::Invalid(drafts) => ValidationOutcome::invalid(
97                drafts
98                    .into_iter()
99                    .map(|draft| Violation::from_draft(rule_id, draft))
100                    .collect(),
101            ),
102        }
103    }
104}