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 fmt::{Debug, Display},
35 marker::PhantomData,
36};
37
38pub const DEFAULT_MIN: usize = 0;
40
41pub const DEFAULT_MAX: usize = 255;
43
44pub const DEFAULT_ASCII_ONLY: bool = false;
46
47pub type GStringNV<const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> =
49 GString<NoValidation, MIN, MAX, ASCII_ONLY>;
50
51pub trait Validator: Clone {
55 type Error;
56
57 fn validate(s: impl AsRef<str>) -> Result<(), Self::Error>;
59}
60
61pub trait AllowEmpty {}
65
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
68pub struct NoValidation;
69
70impl Validator for NoValidation {
71 type Error = Infallible;
72
73 #[inline]
74 fn validate(_: impl AsRef<str>) -> Result<(), Self::Error> {
75 Ok(())
76 }
77}
78
79impl Validator for () {
80 type Error = Infallible;
81
82 #[inline]
83 fn validate(_: impl AsRef<str>) -> Result<(), Self::Error> {
84 Ok(())
85 }
86}
87
88impl AllowEmpty for NoValidation {}
89
90impl AllowEmpty for () {}
91
92#[derive(Copy, Clone, Eq)]
137pub struct GString<
138 V: Validator = NoValidation,
139 const MIN: usize = DEFAULT_MIN,
140 const MAX: usize = DEFAULT_MAX,
141 const ASCII_ONLY: bool = DEFAULT_ASCII_ONLY,
142> {
143 buf: [u8; MAX],
144 len: usize,
145 _validator: PhantomData<V>,
146}
147
148impl GString {
149 #[inline]
159 pub fn try_default<S>(s: S) -> Result<Self, GStringError<Infallible>>
160 where
161 S: AsRef<str>,
162 {
163 Self::try_new(s)
164 }
165}
166
167impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
168 GString<V, MIN, MAX, ASCII_ONLY>
169{
170 #[inline]
180 pub fn try_new<S>(s: S) -> Result<Self, GStringError<V::Error>>
181 where
182 S: AsRef<str>,
183 {
184 let gstring = Self::stack_allocate(s.as_ref())?;
185
186 gstring.check_bounds()?;
187 gstring.check_ascii()?;
188
189 gstring.validate()
190 }
191
192 const fn stack_allocate(s: &str) -> Result<Self, Err> {
194 let bytes = s.as_bytes();
195 let len = bytes.len();
196
197 if len > MAX {
198 return Err(Err::TooLong(MAX));
199 }
200
201 let mut buf = [0u8; MAX];
202 let mut i = 0;
203 while i < len {
204 buf[i] = bytes[i];
205 i += 1;
206 }
207
208 Ok(Self {
209 buf,
210 len,
211 _validator: PhantomData,
212 })
213 }
214
215 #[inline]
217 const fn check_bounds(&self) -> Result<(), Err> {
218 const {
219 assert!(MIN <= MAX, "MIN cannot be bigger than MAX");
220 }
221 if self.len < MIN {
222 return Err(Err::TooShort(MIN));
223 }
224 if self.len > MAX {
225 return Err(Err::TooLong(MAX));
226 }
227
228 Ok(())
229 }
230
231 #[inline]
233 const fn check_ascii(&self) -> Result<(), Err> {
234 if ASCII_ONLY {
235 let mut i = 0;
236 while i < self.len {
237 if self.buf[i] >= 128 {
239 return Err(Err::NotAscii);
240 }
241 i += 1;
242 }
243 }
244
245 Ok(())
246 }
247
248 #[inline(always)]
250 pub fn validate(self) -> Result<Self, GStringError<V::Error>> {
251 V::validate(&self).map_err(GStringError::Validation)?;
252 Ok(self)
253 }
254
255 #[inline]
262 pub fn inspect<F>(self, func: F) -> Self
263 where
264 F: FnOnce(&str),
265 {
266 func(self.as_str());
267 self
268 }
269
270 #[inline]
277 pub fn map<UV, const UMIN: usize, const UMAX: usize, const UASCII_ONLY: bool, F, U>(
278 self,
279 func: F,
280 ) -> Result<GString<UV, UMIN, UMAX, UASCII_ONLY>, GStringError<UV::Error>>
281 where
282 UV: Validator,
283 F: FnOnce(&str) -> U,
284 U: AsRef<str>,
285 {
286 GString::<UV, UMIN, UMAX, UASCII_ONLY>::try_new(func(self.as_str()).as_ref())
287 }
288
289 #[inline]
294 pub fn and_then<UV, const UMIN: usize, const UMAX: usize, const UASCII_ONLY: bool, F>(
295 self,
296 func: F,
297 ) -> Result<GString<UV, UMIN, UMAX, UASCII_ONLY>, GStringError<UV::Error>>
298 where
299 UV: Validator,
300 F: FnOnce(&str) -> Result<GString<UV, UMIN, UMAX, UASCII_ONLY>, GStringError<UV::Error>>,
301 {
302 func(self.as_str())
303 }
304}
305
306#[derive(Debug)]
310pub struct InValidatedGString<
311 V: Validator,
312 const MIN: usize,
313 const MAX: usize,
314 const ASCII_ONLY: bool,
315>(GString<V, MIN, MAX, ASCII_ONLY>);
316
317impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
318 InValidatedGString<V, MIN, MAX, ASCII_ONLY>
319{
320 #[inline(always)]
322 pub fn validate(self) -> Result<GString<V, MIN, MAX, ASCII_ONLY>, GStringError<V::Error>> {
323 self.0.validate()
324 }
325}
326
327macro_rules! errpanic {
328 ($expr:expr) => {
329 match $expr {
330 Ok(v) => v,
331 Err(Err::TooShort(_)) => {
332 panic!("minimum length below MIN")
333 }
334 Err(Err::TooLong(_)) => {
335 panic!("maximum length exceeds MAX")
336 }
337 Err(Err::NotAscii) => {
338 panic!("only ASCII characters are allowed")
339 }
340 }
341 };
342}
343
344impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
345 GString<V, MIN, MAX, ASCII_ONLY>
346{
347 #[allow(clippy::new_ret_no_self)]
350 pub const fn new(s: &str) -> InValidatedGString<V, MIN, MAX, ASCII_ONLY> {
351 let ret = errpanic!(Self::stack_allocate(s));
352 errpanic!(ret.check_bounds());
353 errpanic!(ret.check_ascii());
354 InValidatedGString(ret)
355 }
356}
357
358impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Display
359 for GString<V, MIN, MAX, ASCII_ONLY>
360{
361 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
362 write!(f, "{}", self.as_str())
363 }
364}
365
366impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Debug
367 for GString<V, MIN, MAX, ASCII_ONLY>
368{
369 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
370 write!(
371 f,
372 "GString(\"{}\", MIN={}, MAX={}, ASCII_ONLY={})",
373 self.as_str(),
374 MIN,
375 MAX,
376 ASCII_ONLY
377 )
378 }
379}
380
381impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
382 core::borrow::Borrow<str> for GString<V, MIN, MAX, ASCII_ONLY>
383{
384 #[inline]
385 fn borrow(&self) -> &str {
386 self.as_str()
387 }
388}
389
390impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::hash::Hash
391 for GString<V, MIN, MAX, ASCII_ONLY>
392{
393 #[inline]
394 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
395 self.as_str().hash(state)
396 }
397}
398
399impl<V: Validator + AllowEmpty, const MAX: usize, const ASCII_ONLY: bool> Default
400 for GString<V, 0, MAX, ASCII_ONLY>
401{
402 #[inline]
403 fn default() -> Self {
404 Self {
405 buf: [0u8; MAX],
406 len: 0,
407 _validator: PhantomData,
408 }
409 }
410}
411
412#[cfg(feature = "secret")]
413impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
414 GString<V, MIN, MAX, ASCII_ONLY>
415{
416 pub fn zeroize(&mut self) {
417 use zeroize::Zeroize;
418
419 self.buf.zeroize();
420 self.len.zeroize();
421 }
422}