Skip to main content

qubit_validator/report/
violation_code.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//! Stable validation violation codes.
10
11use std::fmt::Debug;
12use std::fmt::Display;
13use std::fmt::Formatter;
14use std::fmt::Result as FmtResult;
15
16use super::ViolationCodeError;
17use crate::internal::DottedIdentifierError;
18use crate::internal::validate_dotted_identifier;
19
20/// A stable violation code using the point-separated ASCII protocol.
21///
22/// The value is non-empty and consists of one or more dot-separated segments.
23/// Each segment starts with an ASCII letter and continues with ASCII letters,
24/// digits, or underscores. Empty segments, whitespace, non-ASCII letters, and
25/// hyphens are rejected. No trimming or case normalization is performed.
26///
27/// # Examples
28///
29/// ```
30/// use qubit_validator::ViolationCode;
31/// use qubit_validator::ViolationCodeError;
32///
33/// let code = ViolationCode::try_new("text.length.minimum")?;
34/// assert_eq!(code.as_str(), "text.length.minimum");
35/// let error = ViolationCode::try_new("text..minimum").unwrap_err();
36/// assert_eq!(error, ViolationCodeError::EmptySegment);
37/// assert_eq!(
38///     ViolationCode::try_new("text.min-length"),
39///     Err(ViolationCodeError::InvalidSegment),
40/// );
41/// # Ok::<(), ViolationCodeError>(())
42/// ```
43#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct ViolationCode(
45    /// Validated static protocol string.
46    &'static str,
47);
48
49impl ViolationCode {
50    /// Creates a code from a valid static string.
51    ///
52    /// # Parameters
53    ///
54    /// - `value`: A static string containing the complete protocol value.
55    ///
56    /// # Returns
57    ///
58    /// The validated violation code.
59    ///
60    /// # Panics
61    ///
62    /// Panics when `value` violates the syntax documented on [`ViolationCode`].
63    #[must_use]
64    #[inline]
65    pub const fn new(value: &'static str) -> Self {
66        match Self::try_new(value) {
67            Ok(code) => code,
68            Err(_) => panic!("invalid violation code"),
69        }
70    }
71
72    /// Validates and creates a code.
73    ///
74    /// # Parameters
75    ///
76    /// - `value`: A static string containing the complete protocol value.
77    ///
78    /// # Returns
79    ///
80    /// The validated violation code when `value` follows the protocol.
81    ///
82    /// # Errors
83    ///
84    /// Returns [`ViolationCodeError::Empty`] for an empty string,
85    /// [`ViolationCodeError::EmptySegment`] for an empty dot-separated segment,
86    /// or [`ViolationCodeError::InvalidSegment`] for a segment with invalid
87    /// characters or an invalid first character.
88    #[inline]
89    pub const fn try_new(value: &'static str) -> Result<Self, ViolationCodeError> {
90        match validate_dotted_identifier(value) {
91            Ok(()) => Ok(Self(value)),
92            Err(DottedIdentifierError::Empty) => Err(ViolationCodeError::Empty),
93            Err(DottedIdentifierError::EmptySegment) => Err(ViolationCodeError::EmptySegment),
94            Err(DottedIdentifierError::InvalidSegment) => Err(ViolationCodeError::InvalidSegment),
95        }
96    }
97
98    /// Returns the complete stable code.
99    #[must_use]
100    #[inline]
101    pub const fn as_str(self) -> &'static str {
102        self.0
103    }
104}
105
106impl Debug for ViolationCode {
107    /// Formats the validated, program-declared code.
108    ///
109    /// # Parameters
110    ///
111    /// - `formatter`: The formatter receiving the debug tuple representation.
112    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
113        formatter.debug_tuple("ViolationCode").field(&self.0).finish()
114    }
115}
116
117impl Display for ViolationCode {
118    /// Formats the validated, program-declared code.
119    ///
120    /// # Parameters
121    ///
122    /// - `formatter`: The formatter receiving the code's display
123    ///   representation.
124    fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
125        formatter.write_str(self.0)
126    }
127}