1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4mod conversion;
5mod equality;
6mod error;
7mod iterator;
8mod macros;
9mod mutation;
10mod order;
11mod query;
12pub use query::Pattern;
13
14mod pattern_impl;
15
16#[cfg(feature = "secret")]
17mod gsecret;
18
19#[cfg(feature = "secret")]
20pub use gsecret::GSecret;
21
22#[cfg(feature = "grapheme")]
23pub use unicode_segmentation::{Graphemes, UnicodeSegmentation};
24
25#[cfg(feature = "serde")]
26mod serde;
27
28pub use error::GStringError;
29
30use error::Err;
31
32use core::{
33 convert::Infallible,
34 error::Error,
35 fmt::{Debug, Display},
36 marker::PhantomData,
37};
38
39pub const DEFAULT_MIN: usize = 0;
41
42pub const DEFAULT_MAX: usize = 255;
44
45pub const DEFAULT_ASCII_ONLY: bool = false;
47
48pub type GStringNV<const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> =
50 GString<NoValidation, MIN, MAX, ASCII_ONLY>;
51
52pub trait Validator: Clone {
56 type Err: Error + Send + Sync + 'static;
57
58 fn validate(s: impl AsRef<str>) -> Result<(), Self::Err>;
60}
61
62pub trait AllowEmpty {}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq)]
69pub struct NoValidation;
70
71impl Validator for NoValidation {
72 type Err = Infallible;
73
74 fn validate(_: impl AsRef<str>) -> Result<(), Self::Err> {
75 Ok(())
76 }
77}
78
79impl Validator for () {
80 type Err = Infallible;
81
82 fn validate(_: impl AsRef<str>) -> Result<(), Self::Err> {
83 Ok(())
84 }
85}
86
87impl AllowEmpty for NoValidation {}
88
89impl AllowEmpty for () {}
90
91#[derive(Copy, Clone, Eq)]
136pub struct GString<
137 V: Validator = NoValidation,
138 const MIN: usize = DEFAULT_MIN,
139 const MAX: usize = DEFAULT_MAX,
140 const ASCII_ONLY: bool = DEFAULT_ASCII_ONLY,
141> {
142 buf: [u8; MAX],
143 len: usize,
144 _validator: PhantomData<V>,
145}
146
147impl GString {
148 #[inline]
158 pub fn try_default<S>(s: S) -> Result<Self, GStringError<Infallible>>
159 where
160 S: AsRef<str>,
161 {
162 Self::try_new(s)
163 }
164}
165
166impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
167 GString<V, MIN, MAX, ASCII_ONLY>
168{
169 #[inline]
179 pub fn try_new<S>(s: S) -> Result<Self, GStringError<V::Err>>
180 where
181 S: AsRef<str>,
182 {
183 let gstring = Self::stack_allocate(s.as_ref())?;
184
185 gstring.check_bounds()?;
186 gstring.check_ascii()?;
187
188 gstring.validate()
189 }
190
191 const fn stack_allocate(s: &str) -> Result<Self, Err> {
193 let bytes = s.as_bytes();
194 let len = bytes.len();
195
196 if len > MAX {
197 return Err(Err::TooLong(MAX));
198 }
199
200 let mut buf = [0u8; MAX];
201 let mut i = 0;
202 while i < len {
203 buf[i] = bytes[i];
204 i += 1;
205 }
206
207 Ok(Self {
208 buf,
209 len,
210 _validator: PhantomData,
211 })
212 }
213
214 #[inline]
216 const fn check_bounds(&self) -> Result<(), Err> {
217 const {
218 assert!(MIN <= MAX, "MIN cannot be bigger than MAX");
219 }
220 if self.len < MIN {
221 return Err(Err::TooShort(MIN));
222 }
223 if self.len > MAX {
224 return Err(Err::TooLong(MAX));
225 }
226
227 Ok(())
228 }
229
230 #[inline]
232 const fn check_ascii(&self) -> Result<(), Err> {
233 if ASCII_ONLY {
234 let mut i = 0;
235 while i < self.len {
236 if self.buf[i] >= 128 {
238 return Err(Err::NotAscii);
239 }
240 i += 1;
241 }
242 }
243
244 Ok(())
245 }
246
247 #[inline(always)]
249 pub fn validate(self) -> Result<Self, GStringError<V::Err>> {
250 V::validate(&self).map_err(GStringError::Validation)?;
251 Ok(self)
252 }
253}
254
255#[derive(Debug)]
259pub struct InValidatedGString<
260 V: Validator,
261 const MIN: usize,
262 const MAX: usize,
263 const ASCII_ONLY: bool,
264>(GString<V, MIN, MAX, ASCII_ONLY>);
265
266impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
267 InValidatedGString<V, MIN, MAX, ASCII_ONLY>
268{
269 #[inline(always)]
271 pub fn validate(self) -> Result<GString<V, MIN, MAX, ASCII_ONLY>, GStringError<V::Err>> {
272 self.0.validate()
273 }
274}
275
276macro_rules! errpanic {
277 ($expr:expr) => {
278 match $expr {
279 Ok(v) => v,
280 Err(Err::TooShort(_)) => {
281 panic!("minimum length below MIN")
282 }
283 Err(Err::TooLong(_)) => {
284 panic!("maximum length exceeds MAX")
285 }
286 Err(Err::NotAscii) => {
287 panic!("only ASCII characters are allowed")
288 }
289 }
290 };
291}
292
293impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
294 GString<V, MIN, MAX, ASCII_ONLY>
295{
296 #[allow(clippy::new_ret_no_self)]
299 pub const fn new(s: &str) -> InValidatedGString<V, MIN, MAX, ASCII_ONLY> {
300 let ret = errpanic!(Self::stack_allocate(s));
301 errpanic!(ret.check_bounds());
302 errpanic!(ret.check_ascii());
303 InValidatedGString(ret)
304 }
305}
306
307impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Display
308 for GString<V, MIN, MAX, ASCII_ONLY>
309{
310 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
311 write!(f, "{}", self.as_str())
312 }
313}
314
315impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Debug
316 for GString<V, MIN, MAX, ASCII_ONLY>
317{
318 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
319 write!(
320 f,
321 "GString(\"{}\", MIN={}, MAX={}, ASCII_ONLY={})",
322 self.as_str(),
323 MIN,
324 MAX,
325 ASCII_ONLY
326 )
327 }
328}
329
330impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
331 core::borrow::Borrow<str> for GString<V, MIN, MAX, ASCII_ONLY>
332{
333 #[inline]
334 fn borrow(&self) -> &str {
335 self.as_str()
336 }
337}
338
339impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::hash::Hash
340 for GString<V, MIN, MAX, ASCII_ONLY>
341{
342 #[inline]
343 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
344 self.as_str().hash(state)
345 }
346}
347
348impl<V: Validator + AllowEmpty, const MAX: usize, const ASCII_ONLY: bool> Default
349 for GString<V, 0, MAX, ASCII_ONLY>
350{
351 #[inline]
352 fn default() -> Self {
353 Self {
354 buf: [0u8; MAX],
355 len: 0,
356 _validator: PhantomData,
357 }
358 }
359}
360
361#[cfg(feature = "secret")]
362impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
363 GString<V, MIN, MAX, ASCII_ONLY>
364{
365 pub fn zeroize(&mut self) {
366 use zeroize::Zeroize;
367
368 self.buf.zeroize();
369 self.len.zeroize();
370 }
371}