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}