Skip to main content

qubit_codec_misc/
hex_codec_config.rs

1// =============================================================================
2//    Copyright (c) 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Configuration shared by hexadecimal byte codecs.
9
10/// Configuration shared by [`crate::HexCodec`] encoding and decoding
11/// operations.
12///
13/// The configuration is intentionally separate from the codec value so the
14/// same formatting and decoding policy can be reused by multiple codecs.
15///
16/// # Examples
17///
18/// ```
19/// use qubit_codec_misc::HexCodec;
20/// use qubit_codec_misc::HexCodecConfig;
21///
22/// let config = HexCodecConfig::new()
23///     .with_uppercase(true)
24///     .with_prefix("0x");
25/// let codec = HexCodec::from_config(config);
26/// assert_eq!(codec.encode(&[0x1f, 0x8b]), "0x1F8B");
27/// ```
28#[must_use]
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct HexCodecConfig {
31    /// Whether to use uppercase hexadecimal digits while encoding.
32    pub(crate) uppercase: bool,
33    /// The prefix to use before the whole encoded string.
34    pub(crate) prefix: Option<String>,
35    /// The prefix to use before each encoded byte.
36    pub(crate) byte_prefix: Option<String>,
37    /// The separator to use between bytes in the encoded string.
38    pub(crate) separator: Option<String>,
39    /// Whether to ignore ASCII whitespace while decoding.
40    pub(crate) ignore_ascii_whitespace: bool,
41    /// Whether to ignore ASCII case when matching configured prefixes.
42    pub(crate) ignore_prefix_case: bool,
43}
44
45impl HexCodecConfig {
46    /// Creates a lowercase configuration without prefixes or separators.
47    #[inline]
48    pub fn new() -> Self {
49        Self {
50            uppercase: false,
51            prefix: None,
52            byte_prefix: None,
53            separator: None,
54            ignore_ascii_whitespace: false,
55            ignore_prefix_case: false,
56        }
57    }
58
59    /// Sets whether encoded digits should be uppercase.
60    ///
61    /// # Parameters
62    /// - `uppercase`: Whether encoding uses uppercase hexadecimal digits.
63    ///
64    /// # Returns
65    /// The updated configuration.
66    #[inline]
67    pub fn with_uppercase(mut self, uppercase: bool) -> Self {
68        self.uppercase = uppercase;
69        self
70    }
71
72    /// Sets a whole-output prefix.
73    ///
74    /// # Parameters
75    /// - `prefix`: Text written once before all encoded bytes.
76    ///
77    /// # Returns
78    /// The updated configuration.
79    #[inline]
80    pub fn with_prefix(mut self, prefix: impl Into<String>) -> Self {
81        self.prefix = Some(prefix.into());
82        self
83    }
84
85    /// Sets a per-byte prefix.
86    ///
87    /// # Parameters
88    /// - `prefix`: Text written before each encoded byte.
89    ///
90    /// # Returns
91    /// The updated configuration.
92    #[inline]
93    pub fn with_byte_prefix(mut self, prefix: impl Into<String>) -> Self {
94        self.byte_prefix = Some(prefix.into());
95        self
96    }
97
98    /// Sets a separator between encoded bytes.
99    ///
100    /// # Parameters
101    /// - `separator`: Text written between adjacent encoded bytes.
102    ///
103    /// # Returns
104    /// The updated configuration.
105    #[inline]
106    pub fn with_separator(mut self, separator: impl Into<String>) -> Self {
107        self.separator = Some(separator.into());
108        self
109    }
110
111    /// Sets whether ASCII whitespace is ignored while decoding.
112    ///
113    /// # Parameters
114    /// - `ignore`: Whether ASCII whitespace is skipped during decoding.
115    ///
116    /// # Returns
117    /// The updated configuration.
118    #[inline]
119    pub fn with_ignored_ascii_whitespace(mut self, ignore: bool) -> Self {
120        self.ignore_ascii_whitespace = ignore;
121        self
122    }
123
124    /// Sets whether configured prefixes are matched case-insensitively.
125    ///
126    /// # Parameters
127    /// - `ignore`: Whether prefix matching ignores ASCII letter case.
128    ///
129    /// # Returns
130    /// The updated configuration.
131    #[inline]
132    pub fn with_ignore_prefix_case(mut self, ignore: bool) -> Self {
133        self.ignore_prefix_case = ignore;
134        self
135    }
136
137    /// Returns whether encoded digits use uppercase characters.
138    #[must_use]
139    #[inline]
140    pub fn is_uppercase(&self) -> bool {
141        self.uppercase
142    }
143
144    /// Returns the configured whole-output prefix.
145    ///
146    /// # Returns
147    /// `Some` contains the prefix written before the encoded bytes; `None`
148    /// means no whole-output prefix is configured.
149    #[must_use]
150    #[inline]
151    pub fn prefix(&self) -> Option<&str> {
152        self.prefix.as_deref()
153    }
154
155    /// Returns the configured per-byte prefix.
156    ///
157    /// # Returns
158    /// `Some` contains the prefix written before each encoded byte; `None`
159    /// means no per-byte prefix is configured.
160    #[must_use]
161    #[inline]
162    pub fn byte_prefix(&self) -> Option<&str> {
163        self.byte_prefix.as_deref()
164    }
165
166    /// Returns the configured separator.
167    ///
168    /// # Returns
169    /// `Some` contains the separator written between bytes; `None` means no
170    /// separator is configured.
171    #[must_use]
172    #[inline]
173    pub fn separator(&self) -> Option<&str> {
174        self.separator.as_deref()
175    }
176
177    /// Returns whether ASCII whitespace is ignored while decoding.
178    #[must_use]
179    #[inline]
180    pub fn ignores_ascii_whitespace(&self) -> bool {
181        self.ignore_ascii_whitespace
182    }
183
184    /// Returns whether configured prefixes are matched case-insensitively.
185    #[must_use]
186    #[inline]
187    pub fn ignores_prefix_case(&self) -> bool {
188        self.ignore_prefix_case
189    }
190}
191
192impl Default for HexCodecConfig {
193    #[inline]
194    fn default() -> Self {
195        Self::new()
196    }
197}