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;
11
12#[cfg(feature = "secret")]
13mod gsecret;
14
15#[cfg(feature = "secret")]
16pub use gsecret::GSecret;
17
18pub use macros::NotValidatedGString;
19
20#[cfg(feature = "serde")]
21mod serde;
22
23pub use error::GStringError;
24
25use error::Err;
26
27use core::{
28 convert::Infallible,
29 error::Error,
30 fmt::{Debug, Display},
31 marker::PhantomData,
32};
33
34pub const DEFAULT_MIN: usize = 0;
36
37pub const DEFAULT_MAX: usize = 255;
39
40pub const DEFAULT_ASCII_ONLY: bool = false;
42
43pub type GStringNV<const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> =
45 GString<NoValidation, MIN, MAX, ASCII_ONLY>;
46
47pub trait Validator: Clone {
51 type Err: Error + Send + Sync + 'static;
52
53 fn validate(s: impl AsRef<str>) -> Result<(), Self::Err>;
55}
56
57pub trait AllowEmpty {}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub struct NoValidation;
65
66impl Validator for NoValidation {
67 type Err = Infallible;
68
69 fn validate(_: impl AsRef<str>) -> Result<(), Self::Err> {
70 Ok(())
71 }
72}
73
74impl Validator for () {
75 type Err = Infallible;
76
77 fn validate(_: impl AsRef<str>) -> Result<(), Self::Err> {
78 Ok(())
79 }
80}
81
82impl AllowEmpty for NoValidation {}
83
84impl AllowEmpty for () {}
85
86#[derive(Copy, Clone, Eq)]
131pub struct GString<
132 V: Validator = NoValidation,
133 const MIN: usize = DEFAULT_MIN,
134 const MAX: usize = DEFAULT_MAX,
135 const ASCII_ONLY: bool = DEFAULT_ASCII_ONLY,
136> {
137 buf: [u8; MAX],
138 len: usize,
139 _validator: PhantomData<V>,
140}
141
142impl GString {
143 #[inline]
153 pub fn try_default<S>(s: S) -> Result<Self, GStringError<Infallible>>
154 where
155 S: AsRef<str>,
156 {
157 Self::try_new(s)
158 }
159}
160
161impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
162 GString<V, MIN, MAX, ASCII_ONLY>
163{
164 #[inline]
174 pub fn try_new<S>(s: S) -> Result<Self, GStringError<V::Err>>
175 where
176 S: AsRef<str>,
177 {
178 let gstring = Self::stack_allocate(s.as_ref())?;
179
180 gstring.check_bounds()?;
181 gstring.check_ascii()?;
182
183 gstring.validate()
184 }
185
186 const fn stack_allocate(s: &str) -> Result<Self, Err> {
188 let bytes = s.as_bytes();
189 let len = bytes.len();
190
191 if len > MAX {
192 return Err(Err::TooLong(MAX));
193 }
194
195 let mut buf = [0u8; MAX];
196 let mut i = 0;
197 while i < len {
198 buf[i] = bytes[i];
199 i += 1;
200 }
201
202 Ok(Self {
203 buf,
204 len,
205 _validator: PhantomData,
206 })
207 }
208
209 #[inline]
211 const fn check_bounds(&self) -> Result<(), Err> {
212 const {
213 assert!(MIN <= MAX, "MIN cannot be bigger than MAX");
214 }
215 if self.len < MIN {
216 return Err(Err::TooShort(MIN));
217 }
218 if self.len > MAX {
219 return Err(Err::TooLong(MAX));
220 }
221
222 Ok(())
223 }
224
225 #[inline]
227 const fn check_ascii(&self) -> Result<(), Err> {
228 if ASCII_ONLY {
229 let mut i = 0;
230 while i < self.len {
231 if self.buf[i] >= 128 {
233 return Err(Err::NotAscii);
234 }
235 i += 1;
236 }
237 }
238
239 Ok(())
240 }
241
242 #[inline(always)]
244 pub fn validate(self) -> Result<Self, GStringError<V::Err>> {
245 V::validate(&self).map_err(GStringError::Validation)?;
246 Ok(self)
247 }
248
249 #[inline]
251 pub const fn as_str(&self) -> &str {
252 unsafe {
255 let slice = core::slice::from_raw_parts(self.buf.as_ptr(), self.len);
256 core::str::from_utf8_unchecked(slice)
257 }
258 }
259
260 #[inline]
262 pub const fn len(&self) -> usize {
263 self.len
264 }
265
266 #[inline]
268 pub fn count(&self) -> usize {
269 self.chars().count()
270 }
271
272 #[cfg(feature = "grapheme")]
274 pub fn grapheme_count(&self) -> usize {
275 use unicode_segmentation::UnicodeSegmentation;
276 self.graphemes(true).count()
277 }
278
279 #[inline]
281 pub const fn is_empty(&self) -> bool {
282 self.len == 0
283 }
284
285 #[inline]
287 pub const fn capacity(&self) -> usize {
288 MAX
289 }
290
291 #[inline]
293 pub const fn is_full(&self) -> bool {
294 self.len == MAX
295 }
296}
297
298impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Display
299 for GString<V, MIN, MAX, ASCII_ONLY>
300{
301 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
302 write!(f, "{}", self.as_str())
303 }
304}
305
306impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Debug
307 for GString<V, MIN, MAX, ASCII_ONLY>
308{
309 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
310 write!(
311 f,
312 "GString(\"{}\", MIN={}, MAX={}, ASCII_ONLY={})",
313 self.as_str(),
314 MIN,
315 MAX,
316 ASCII_ONLY
317 )
318 }
319}
320
321impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::ops::Deref
322 for GString<V, MIN, MAX, ASCII_ONLY>
323{
324 type Target = str;
325
326 #[inline]
327 fn deref(&self) -> &Self::Target {
328 self.as_str()
329 }
330}
331
332impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
333 core::borrow::Borrow<str> for GString<V, MIN, MAX, ASCII_ONLY>
334{
335 #[inline]
336 fn borrow(&self) -> &str {
337 self.as_str()
338 }
339}
340
341impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::hash::Hash
342 for GString<V, MIN, MAX, ASCII_ONLY>
343{
344 #[inline]
345 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
346 self.as_str().hash(state)
347 }
348}
349
350impl<V: Validator + AllowEmpty, const MAX: usize, const ASCII_ONLY: bool> Default
351 for GString<V, 0, MAX, ASCII_ONLY>
352{
353 #[inline]
354 fn default() -> Self {
355 Self {
356 buf: [0u8; MAX],
357 len: 0,
358 _validator: PhantomData,
359 }
360 }
361}
362
363#[cfg(feature = "secret")]
364impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
365 GString<V, MIN, MAX, ASCII_ONLY>
366{
367 pub fn zeroize(&mut self) {
368 use zeroize::Zeroize;
369
370 self.buf.zeroize();
371 self.len.zeroize();
372 }
373}