1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3
4use core::{
5 borrow::Borrow,
6 cmp::PartialOrd,
7 convert::Infallible,
8 error::Error,
9 fmt,
10 hash::{Hash, Hasher},
11 marker::PhantomData,
12};
13
14pub mod aliases;
15
16#[cfg(feature = "serde")]
17mod serde;
18
19pub trait Validator<T> {
56 type Target: PartialOrd<Self::Target> + PartialOrd<T> + ?Sized + 'static;
63
64 type Error;
66
67 #[inline]
69 fn min() -> Option<&'static Self::Target> {
70 None
71 }
72
73 #[inline]
75 fn max() -> Option<&'static Self::Target> {
76 None
77 }
78
79 #[inline]
81 fn validate(_: &T) -> Result<(), Self::Error> {
82 Ok(())
83 }
84}
85
86#[derive(Debug, Clone, Copy)]
96pub struct NoValidation;
97
98impl<T: PartialOrd + 'static> Validator<T> for NoValidation {
99 type Target = T;
100 type Error = Infallible;
101}
102
103impl<T: PartialOrd + 'static> Validator<T> for () {
104 type Target = T;
105 type Error = Infallible;
106}
107
108#[non_exhaustive]
110#[derive(Debug, Clone, PartialEq, Eq)]
111pub enum GTypeError<E> {
112 MinExceedsMax,
114
115 BelowMinimum,
117
118 AboveMaximum,
120
121 Validation(E),
123}
124
125impl<E: fmt::Display> fmt::Display for GTypeError<E> {
126 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
127 match self {
128 Self::MinExceedsMax => f.write_str("minimum value exceeds maximum value"),
129 Self::BelowMinimum => f.write_str("value is below minimum"),
130 Self::AboveMaximum => f.write_str("value is above maximum"),
131 Self::Validation(err) => write!(f, "validation error: {}", err),
132 }
133 }
134}
135
136impl<E: Error + 'static> Error for GTypeError<E> {
137 fn source(&self) -> Option<&(dyn Error + 'static)> {
138 match self {
139 Self::Validation(err) => Some(err),
140 Self::MinExceedsMax | Self::BelowMinimum | Self::AboveMaximum => None,
141 }
142 }
143}
144
145#[repr(transparent)]
185pub struct GType<T, V = NoValidation> {
186 value: T,
187 _marker: PhantomData<V>,
188}
189
190impl<T: PartialOrd<V::Target>, V: Validator<T>> GType<T, V> {
191 #[inline]
192 pub(crate) const fn new(value: T) -> Self {
193 Self {
194 value,
195 _marker: PhantomData,
196 }
197 }
198
199 pub fn try_new(value: T) -> Result<Self, GTypeError<V::Error>> {
204 #[allow(clippy::neg_cmp_op_on_partial_ord)]
205 match (V::min(), V::max()) {
206 (Some(min), Some(max)) if !(min <= max) => {
207 return Err(GTypeError::MinExceedsMax);
208 }
209 (Some(min), _) if !(&value >= min) => {
210 return Err(GTypeError::BelowMinimum);
211 }
212 (_, Some(max)) if !(&value <= max) => {
213 return Err(GTypeError::AboveMaximum);
214 }
215 _ => (),
216 }
217
218 V::validate(&value).map_err(GTypeError::Validation)?;
219
220 Ok(Self::new(value))
221 }
222
223 #[inline]
225 pub const fn as_ref(&self) -> &T {
226 &self.value
227 }
228
229 #[inline]
231 pub fn into_inner(self) -> T {
232 self.value
233 }
234
235 #[inline]
242 pub fn inspect<F>(self, func: F) -> Self
243 where
244 F: FnOnce(&T),
245 {
246 func(&self.value);
247 self
248 }
249
250 #[inline]
257 pub fn map<U, UV, F>(self, func: F) -> Result<GType<U, UV>, GTypeError<UV::Error>>
258 where
259 U: PartialOrd<UV::Target>,
260 UV: Validator<U>,
261 F: FnOnce(T) -> U,
262 {
263 GType::<U, UV>::try_new(func(self.value))
264 }
265
266 #[inline]
271 pub fn and_then<U, UV, F>(self, func: F) -> Result<GType<U, UV>, GTypeError<UV::Error>>
272 where
273 U: PartialOrd<UV::Target>,
274 UV: Validator<U>,
275 F: FnOnce(T) -> Result<GType<U, UV>, GTypeError<UV::Error>>,
276 {
277 func(self.value)
278 }
279}
280
281impl<T, V> AsRef<T> for GType<T, V> {
282 #[inline]
283 fn as_ref(&self) -> &T {
284 &self.value
285 }
286}
287
288impl<T, V> Borrow<T> for GType<T, V> {
289 #[inline]
290 fn borrow(&self) -> &T {
291 &self.value
292 }
293}
294
295impl<T, V> Clone for GType<T, V>
296where
297 T: Clone,
298{
299 #[inline]
300 fn clone(&self) -> Self {
301 Self {
302 value: self.value.clone(),
303 _marker: PhantomData,
304 }
305 }
306}
307
308impl<T, V> Copy for GType<T, V> where T: Copy {}
309
310impl<T, V> fmt::Debug for GType<T, V>
311where
312 T: fmt::Debug,
313{
314 #[inline]
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 self.value.fmt(f)
317 }
318}
319
320impl<T, V> fmt::Display for GType<T, V>
321where
322 T: fmt::Display,
323{
324 #[inline]
325 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
326 self.value.fmt(f)
327 }
328}
329
330impl<T, U, LHSV, RHSV> PartialEq<GType<U, RHSV>> for GType<T, LHSV>
331where
332 T: PartialEq<U>,
333 U: PartialEq<T>,
334{
335 #[inline]
336 fn eq(&self, other: >ype<U, RHSV>) -> bool {
337 self.value == other.value
338 }
339}
340
341impl<T, V> Eq for GType<T, V> where T: Eq {}
342
343impl<T, U, LHSV, RHSV> PartialOrd<GType<U, RHSV>> for GType<T, LHSV>
344where
345 T: PartialOrd<U>,
346 U: PartialOrd<T>,
347{
348 #[inline]
349 fn partial_cmp(&self, other: >ype<U, RHSV>) -> Option<core::cmp::Ordering> {
350 self.value.partial_cmp(&other.value)
351 }
352}
353
354impl<T, V> Ord for GType<T, V>
355where
356 T: Ord,
357{
358 #[inline]
359 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
360 self.value.cmp(&other.value)
361 }
362}
363
364impl<T, V> Hash for GType<T, V>
365where
366 T: Hash,
367{
368 #[inline]
369 fn hash<H: Hasher>(&self, state: &mut H) {
370 self.value.hash(state);
371 }
372}