Skip to main content

tstr/
tstr_trait.rs

1use crate::{__TStrRepr, TStr};
2
3use core::{
4    cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd},
5    fmt::{Debug, Display},
6    hash::Hash,
7};
8
9use typewit::Identity;
10
11macro_rules! serde_support {($($serde_bounds:tt)*) => (
12
13/// Many associated items of the [`TStr`] type-level string,
14/// as well as supertraits for traits implemented by it.
15///
16/// This trait is sealed and cannot be implemented outside of the `tstr` crate.
17///
18/// # Serde
19///
20/// This trait has `serde::{Serialize, Deserialize}` as supertraits when
21/// the `"serde"` feature is enabled.
22///
23pub trait IsTStr:
24    Identity<Type = TStr<<Self as IsTStr>::Arg>>
25    + 'static
26    + crate::strlike::StrLike<__TStr = Self>
27    + Copy
28    + Clone
29    + Debug
30    + Display
31    + Default
32    + Hash
33    + Eq
34    + Ord
35    + PartialEq
36    + PartialEq<str>
37    + for<'a> PartialEq<&'a str>
38    + for<'a, 'b> PartialEq<&'a &'b str>
39    + PartialOrd
40    + PartialOrd<str>
41    + for<'a, 'b> PartialOrd<&'a &'b str>
42    + Send
43    + Sized
44    + Sync
45    + core::marker::Unpin
46    $($serde_bounds)*
47{
48    /// The type parameter of `TStr`
49    type Arg: TStrArg;
50
51    /// Constructs this `IsTStr`
52    const VAL: Self;
53
54    /// The length of this string when encoded to utf8
55    const LENGTH: usize;
56
57    /// This string converted to a uf8-encoded byte slice
58    const BYTES: &[u8];
59
60    /// This type-level string converted to a string
61    const STR: &str;
62
63    /// Coerces `Self` to `TStr<Self::Arg>`, only necessary in generic contexts
64    ///
65    /// The const equivalent of this trait method is the
66    /// [`TStr::from_gen`](crate::TStr::from_gen) constructor.
67    ///
68    /// While it's always possible to construct a `TStr` through its
69    /// [`new`](crate::TStr::new) constructor,
70    /// this method ensures that it's the same string as `Self`.
71    ///
72    /// # Example
73    ///
74    /// ```rust
75    /// use tstr::{IsTStr, TStr};
76    ///
77    /// #[repr(transparent)]
78    /// struct Foo<T, N: IsTStr> {
79    ///     val: T,
80    ///     // since TStr is zero-sized, it can be put in `#[repr(transparent)]` types
81    ///     // next to the wrapped non-Zero-Sized-Type.
82    ///     name: TStr<N::Arg>,
83    /// }
84    ///
85    /// impl<T, N: IsTStr> Foo<T, N> {
86    ///     pub fn new(val: T, tstr: N) -> Self {
87    ///         Self{ val, name: tstr.to_tstr() }
88    ///     }
89    /// }
90    /// ```
91    ///
92    fn to_tstr(self) -> TStr<Self::Arg> {
93        <Self as Identity>::TYPE_EQ.to_right(self)
94    }
95
96    /// Coerces a `TStr` into `Self`, only necessary in generic contexts.
97    ///
98    /// The const equivalent of this trait method is the
99    /// [`TStr::to_gen`](crate::TStr::to_gen) method.
100    ///
101    /// While it's always possible to construct `Self` through the
102    /// [`VAL`](crate::IsTStr::VAL) associated constant,
103    /// this function ensures that it's the same string as the argument.
104    ///
105    /// # Example
106    ///
107    /// ```rust
108    /// use tstr::{IsTStr, TStr};
109    ///
110    /// #[repr(transparent)]
111    /// struct Foo<T, N: IsTStr> {
112    ///     val: T,
113    ///     name: TStr<N::Arg>,
114    /// }
115    ///
116    /// impl<T, N: IsTStr> Foo<T, N> {
117    ///     fn name(&self) -> N {
118    ///         N::from_tstr(self.name)
119    ///     }
120    /// }
121    /// ```
122    ///
123    fn from_tstr(tstr: TStr<Self::Arg>) -> Self {
124        <Self as Identity>::TYPE_EQ.to_left(tstr)
125    }
126
127    /// Gets the length of the string in utf8
128    ///
129    /// The const equivalent of this trait is the
130    /// [`tstr::len`](crate::len) function.
131    ///
132    /// # Example
133    ///
134    /// ```rust
135    /// use tstr::{IsTStr, ts};
136    ///
137    /// assert!(ts!(4).len() == 1);
138    ///
139    /// assert!(ts!("hello").len() == 5);
140    ///
141    /// assert!(ts!(rustacean).len() == 9);
142    ///
143    /// ```
144    fn len(self) -> usize {
145        Self::LENGTH
146    }
147
148    /// Gets the `&'static str` equivalent of this [`TStr`]
149    ///
150    /// The const equivalent of this trait is the
151    /// [`tstr::to_str`](crate::to_str) function.
152    ///
153    /// # Example
154    ///
155    /// ```rust
156    /// use tstr::{IsTStr, TStr, ts};
157    ///
158    /// let foo: TStr<_> = ts!(foo);
159    /// assert_eq!(foo.to_str(), "foo");
160    ///
161    /// let bar_str: &str = ts!("bar").to_str();
162    /// assert_eq!(bar_str, "bar");
163    ///
164    /// ```
165    ///
166    fn to_str(self) -> &'static str {
167        Self::STR
168    }
169
170    /// Gets the `&'static [u8]` equivalent of this [`TStr`]
171    ///
172    /// The const equivalent of this trait is the
173    /// [`tstr::to_bytes`](crate::to_bytes) function.
174    ///
175    /// # Example
176    ///
177    /// ```rust
178    /// use tstr::{IsTStr, TStr, ts};
179    ///
180    /// let foo: TStr<_> = ts!(foo);
181    /// assert_eq!(foo.to_bytes(), "foo".as_bytes());
182    ///
183    /// let bar_str: &[u8] = ts!("bar").to_bytes();
184    /// assert_eq!(bar_str, "bar".as_bytes());
185    ///
186    /// ```
187    ///
188    fn to_bytes(self) -> &'static [u8] {
189        Self::BYTES
190    }
191
192    /// Compares two [`TStr`]s for equality
193    ///
194    /// The const equivalent of this trait is the
195    /// [`tstr::eq`](crate::eq) function.
196    ///
197    /// This method exists to allow comparing any `TStr` to any other,
198    /// because the `for<Rhs: IsTStr> PartialEq<Rhs>` supertrait can't be written on stable.
199    ///
200    /// # Examples
201    ///
202    /// ```rust
203    /// use tstr::{IsTStr, ts};
204    ///
205    /// assert!( ts!("foo").tstr_eq(ts!("foo")));
206    ///
207    /// assert!(!ts!("foo").tstr_eq(ts!("bar")));
208    ///
209    /// ```
210    ///
211    fn tstr_eq<Rhs: IsTStr>(self, rhs: Rhs) -> bool {
212        crate::eq(self, rhs)
213    }
214
215    /// Compares two [`TStr`]s for inequality
216    ///
217    /// The const equivalent of this trait is the
218    /// [`tstr::ne`](crate::ne) function.
219    ///
220    /// This method exists to allow comparing any `TStr` to any other,
221    /// because the `for<Rhs: IsTStr> PartialEq<Rhs>` supertrait can't be written on stable.
222    ///
223    /// # Examples
224    ///
225    /// ```rust
226    /// use tstr::{IsTStr, ts};
227    ///
228    /// assert!(!ts!("foo").tstr_ne(ts!("foo")));
229    ///
230    /// assert!( ts!("foo").tstr_ne(ts!("bar")));
231    ///
232    /// ```
233    ///
234    fn tstr_ne<Rhs: IsTStr>(self, rhs: Rhs) -> bool {
235        crate::ne(self, rhs)
236    }
237
238    /// Compares two [`TStr`]s for ordering
239    ///
240    /// The const equivalent of this trait is the
241    /// [`tstr::cmp`](crate::cmp) function.
242    ///
243    /// This method exists to allow comparing any `TStr` to any other,
244    /// because the `for<Rhs: IsTStr> PartialOrd<Rhs>` supertrait can't be written on stable.
245    ///
246    /// # Examples
247    ///
248    /// ```rust
249    /// use tstr::{IsTStr, ts};
250    /// use core::cmp::Ordering;
251    ///
252    /// assert_eq!(ts!("foo").tstr_cmp(ts!("foo")), Ordering::Equal);
253    ///
254    /// assert_eq!(ts!("foo").tstr_cmp(ts!("bar")), Ordering::Greater);
255    ///
256    /// assert_eq!(ts!("bar").tstr_cmp(ts!("foo")), Ordering::Less);
257    ///
258    /// ```
259    ///
260    fn tstr_cmp<Rhs: IsTStr>(self, rhs: Rhs) -> Ordering {
261        crate::cmp(self, rhs)
262    }
263
264    /// Compares two [`TStr`]s for equality,
265    /// returning a proof of (in)equality of `Self` and `Rhs`
266    ///
267    /// The const equivalent of this trait is the
268    /// [`tstr::type_eq`](crate::type_eq) function.
269    ///
270    /// # Example
271    ///
272    /// ```rust
273    /// use tstr::{IsTStr, TStr, TS, ts};
274    /// use tstr::typewit::TypeCmp;
275    ///
276    ///
277    /// assert_eq!(is_right_guess(Guess(ts!(foo))), None);
278    /// assert_eq!(is_right_guess(Guess(ts!(bar))), None);
279    /// assert_eq!(is_right_guess(Guess(ts!(world))), None);
280    ///
281    /// assert!(is_right_guess(Guess(ts!(hello))).is_some_and(|x| x.0 == "hello"));
282    ///
283    /// #[derive(Debug, PartialEq, Eq)]
284    /// struct Guess<S: IsTStr>(S);
285    ///
286    /// fn is_right_guess<S: IsTStr>(guess: Guess<S>) -> Option<Guess<impl IsTStr>> {
287    ///     let ret: Option<Guess<TS!(hello)>> = typecast_guess(guess).ok();
288    ///     ret
289    /// }
290    ///
291    /// /// Coerces `Guess<A>` to `Guess<B>` if `A == B`, returns `Err(guess)` if `A != B`.
292    /// fn typecast_guess<A, B>(guess: Guess<A>) -> Result<Guess<B>, Guess<A>>
293    /// where
294    ///     A: IsTStr,
295    ///     B: IsTStr,
296    /// {
297    ///     tstr::typewit::type_fn!{
298    ///         // type-level function from `S` to `Guess<S>`
299    ///         struct GuessFn;
300    ///         impl<S: IsTStr> S => Guess<S>
301    ///     }
302    ///
303    ///     match A::VAL.type_eq(B::VAL) {
304    ///         TypeCmp::Eq(te) => Ok(
305    ///             // te is a `TypeEq<A, B>`, a value-level proof that both args are the same type.
306    ///             te
307    ///             .map(GuessFn)    // : TypeEq<Guess<A>, Guess<B>>
308    ///             .to_right(guess) // : Guess<B>
309    ///         ),
310    ///         TypeCmp::Ne(_) => Err(guess),
311    ///     }
312    /// }
313    ///
314    ///
315    /// ```
316    ///
317    fn type_eq<Rhs: IsTStr>(self, rhs: Rhs) -> typewit::TypeCmp<Self, Rhs> {
318        crate::type_eq(self, rhs)
319    }
320}
321
322)}
323
324#[cfg(feature = "serde")]
325serde_support! {+ serde::Serialize + serde::de::DeserializeOwned}
326
327#[cfg(not(feature = "serde"))]
328serde_support! {}
329
330impl<S> IsTStr for TStr<S>
331where
332    S: TStrArg,
333{
334    type Arg = S;
335
336    const VAL: Self = Self::new();
337
338    const LENGTH: usize = S::__LENGTH;
339
340    const BYTES: &[u8] = S::__BYTES;
341
342    const STR: &str = S::__STR;
343}
344
345/// For bounding the type parameter of [`TStr`].
346///
347/// You only need this trait if you're using using `TStr` explicitly in the code,
348/// it's usually better have a type parameter bounded by
349/// the [`IsTStr`] trait instead of using `TStr` directly.
350///
351/// This trait is sealed and cannot be implemented outside of the `tstr` crate.
352///
353/// # Example
354///
355/// This example shows a usecase where you'll need to use this trait,
356/// implementing traits for `TStr`.
357///
358/// ```rust
359/// use tstr::{IsTStr, TStr, TStrArg, ts};
360///
361/// assert_eq!("hello".my_as_str(), "hello");
362/// assert_eq!(ts!(world).my_as_str(), "world");
363///
364///
365/// trait MyAsStr {
366///     fn my_as_str(&self) -> &str;
367/// }
368///
369/// impl MyAsStr for &str {
370///     fn my_as_str(&self) -> &str { self }
371/// }
372///
373/// impl<S: TStrArg> MyAsStr for TStr<S> {
374///     fn my_as_str(&self) -> &str { self.to_str() }
375/// }
376/// ```
377///
378pub trait TStrArg: __TStrRepr + 'static {
379    /// Implementation detail
380    #[doc(hidden)]
381    const __LENGTH: usize;
382
383    /// Implementation detail
384    #[doc(hidden)]
385    const __BYTES: &[u8];
386
387    /// Implementation detail
388    #[doc(hidden)]
389    const __STR: &str;
390
391    /// Implementation detail
392    #[doc(hidden)]
393    type __WithRhs<Rhs: TStrArg>: __TStrArgBinary<Lhs = Self, Rhs = Rhs>;
394
395    /// Implementation detail
396    #[cfg(feature = "str_generics")]
397    #[doc(hidden)]
398    type __WithLhsArgs<const LEFT_S: &'static str>: __TStrArgBinary<Lhs = crate::___<LEFT_S>, Rhs = Self>;
399
400    /// Implementation detail
401    #[cfg(not(feature = "str_generics"))]
402    #[doc(hidden)]
403    type __WithLhsArgs<LeftS: __TStrRepr, const LEFT_LEN: usize>: __TStrArgBinary<Lhs = crate::___<LeftS, LEFT_LEN>, Rhs = Self>;
404}
405
406// implemented for `(Lhs, Rhs)`, does binary operations on a pair of type arguments of TStrs
407#[doc(hidden)]
408pub trait __TStrArgBinary {
409    #[doc(hidden)]
410    type Lhs: __TStrRepr;
411
412    #[doc(hidden)]
413    type Rhs: __TStrRepr;
414
415    #[doc(hidden)]
416    const __EQ: bool;
417
418    #[doc(hidden)]
419    const __CMP: core::cmp::Ordering;
420
421    #[doc(hidden)]
422    const __TYPE_CMP: typewit::TypeCmp<crate::TStr<Self::Lhs>, crate::TStr<Self::Rhs>>;
423}
424
425pub(crate) type __ToTStrArgBinary<L, R> = <L as TStrArg>::__WithRhs<R>;
426
427typewit::inj_type_fn! {
428    pub(crate) struct TStrFn;
429
430    impl<S> S => crate::TStr<S>;
431}