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