1use core::fmt;
4
5use paste::paste;
6use thiserror::Error;
7
8use crate::{core::Predicate, logic::And};
9
10pub type Base = u32;
12
13pub const DEFAULT_BASE: Base = 10;
15
16#[derive(Debug, Error)]
18#[error("non-digit character in base `{base}`")]
19pub struct NonDigitError {
20 pub base: Base,
22}
23
24impl NonDigitError {
25 #[must_use]
27 pub const fn new(base: Base) -> Self {
28 Self { base }
29 }
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
34pub struct IsDigit<const B: Base = DEFAULT_BASE>;
35
36pub const OCT_BASE: Base = 8;
38
39pub const HEX_BASE: Base = 16;
41
42pub type IsOctDigit = IsDigit<OCT_BASE>;
44
45pub type IsHexDigit = IsDigit<HEX_BASE>;
47
48impl<const B: Base> Predicate<char> for IsDigit<B> {
49 type Error = NonDigitError;
50
51 fn check(value: &char) -> Result<(), Self::Error> {
52 if value.is_digit(B) {
53 Ok(())
54 } else {
55 Err(Self::Error::new(B))
56 }
57 }
58
59 fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(formatter, "digit in base {B}")
61 }
62}
63
64macro_rules! predicate {
65 (
66 Name = $name: ident,
67 Check = $check: ident,
68 Doc = $doc: expr,
69 Error = $error: expr,
70 Message = $message: expr,
71 Expected = $expected: expr,
72 ) => {
73 paste! {
74 #[derive(Debug, Error, Default)]
75 #[error($message)]
76 #[doc = $error]
77 pub struct [<Non $name Error>];
78
79 impl [<Non $name Error>] {
80 pub const fn new() -> Self {
82 Self
83 }
84 }
85
86 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
87 #[doc = $doc]
88 pub struct [< Is $name >];
89
90 impl Predicate<char> for [< Is $name >] {
91 type Error = [<Non $name Error>];
92
93 fn check(value: &char) -> Result<(), Self::Error> {
94 if value.$check() {
95 Ok(())
96 } else {
97 Err(Self::Error::new())
98 }
99 }
100
101 fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
102 write!(formatter, $expected)
103 }
104 }
105 }
106 };
107}
108
109predicate! {
110 Name = Ascii,
111 Check = is_ascii,
112 Doc = "Checks whether the given character is within the ASCII range.",
113 Error = "Non-ASCII character encountered.",
114 Message = "non-ascii character",
115 Expected = "ascii character",
116}
117
118predicate! {
119 Name = Alphabetic,
120 Check = is_alphabetic,
121 Doc = "Checks whether the given character is alphabetic.",
122 Error = "Non-alphabetic character encountered.",
123 Message = "non-alphabetic character",
124 Expected = "alphabetic character",
125}
126
127predicate! {
128 Name = Alphanumeric,
129 Check = is_alphanumeric,
130 Doc = "Checks whether the given character is alphanumeric.",
131 Error = "Non-alphanumeric character encountered.",
132 Message = "non-alphanumeric character",
133 Expected = "alphanumeric character",
134}
135
136predicate! {
137 Name = Control,
138 Check = is_control,
139 Doc = "Checks whether the given character is control.",
140 Error = "Non-control character encountered.",
141 Message = "non-control character",
142 Expected = "control character",
143}
144
145predicate! {
146 Name = Numeric,
147 Check = is_numeric,
148 Doc = "Checks whether the given character is numeric.",
149 Error = "Non-numeric character encountered.",
150 Message = "non-numeric character",
151 Expected = "numeric character",
152}
153
154predicate! {
155 Name = Lowercase,
156 Check = is_lowercase,
157 Doc = "Checks whether the given character is lowercase.",
158 Error = "Non-lowercase character encountered.",
159 Message = "non-lowercase character",
160 Expected = "lowercase character",
161}
162
163predicate! {
164 Name = Uppercase,
165 Check = is_uppercase,
166 Doc = "Checks whether the given character is uppercase.",
167 Error = "Non-uppercase character encountered.",
168 Message = "non-uppercase character",
169 Expected = "uppercase character",
170}
171
172predicate! {
173 Name = Whitespace,
174 Check = is_whitespace,
175 Doc = "Checks whether the given character is whitespace.",
176 Error = "Non-whitespace character encountered.",
177 Message = "non-whitespace character",
178 Expected = "whitespace character",
179}
180
181pub type IsAsciiAlphabetic = And<IsAscii, IsAlphabetic>;
183
184pub type IsAsciiAlphanumeric = And<IsAscii, IsAlphanumeric>;
186
187pub type IsAsciiControl = And<IsAscii, IsControl>;
189
190pub type IsAsciiNumeric = And<IsAscii, IsNumeric>;
192
193pub type IsAsciiLowercase = And<IsAscii, IsLowercase>;
195
196pub type IsAsciiUppercase = And<IsAscii, IsUppercase>;
198
199pub type IsAsciiWhitespace = And<IsAscii, IsWhitespace>;