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}