tstr/lib.rs
1//! An encoding of type-level strings, with the [`TStr`] type and related macros.
2//!
3//! This crate features all these on stable:
4//! - a relatively readable default representation of type-level strings
5//! based on `char` const parameters.
6//! - items for converting type-level strings to `&'static str` and `&'static [u8]`
7//! - functions for comparing type-level strings to each other and `&str`
8//! - macros for asserting the (in)equality of type-level strings to each other and `&str`
9//!
10//! All of the above functionality can be used in const contexts.
11//!
12//! # Examples
13//!
14//! ### Indexing
15//!
16//! This example demonstrates how you can use type-level strings,
17//! and the [`Index`] trait, to access fields of generic types by name.
18//!
19//! ```rust
20//! use std::ops::Index;
21//!
22//! use tstr::{TS, ts};
23//!
24//! fn main(){
25//! takes_person(&Person::new("Bob".into(), "Marley".into()));
26//!
27//! takes_person(&OtherPerson::new("Bob", "Marley"));
28//! }
29//!
30//! fn takes_person<P>(pers: &P)
31//! where
32//! P: Index<TS!(name), Output = str> + Index<TS!(surname), Output = str>
33//! {
34//! assert_eq!(&pers[ts!(name)], "Bob");
35//! assert_eq!(&pers[ts!(surname)], "Marley");
36//! }
37//!
38//!
39//! use person::Person;
40//! mod person {
41//! use std::ops::Index;
42//!
43//! use tstr::TS;
44//!
45//! pub struct Person {
46//! name: String,
47//! surname: String,
48//! }
49//!
50//! impl Person {
51//! pub fn new(name: String, surname: String) -> Self {
52//! Self{name, surname}
53//! }
54//! }
55//!
56//! impl Index<TS!(name)> for Person {
57//! type Output = str;
58//!
59//! fn index(&self, _: TS!(name)) -> &str {
60//! &self.name
61//! }
62//! }
63//!
64//! impl Index<TS!(surname)> for Person {
65//! type Output = str;
66//!
67//! fn index(&self, _: TS!(surname)) -> &str {
68//! &self.surname
69//! }
70//! }
71//! }
72//!
73//! use other_person::OtherPerson;
74//! mod other_person {
75//! use std::ops::Index;
76//!
77//! use tstr::TS;
78//!
79//! pub struct OtherPerson {
80//! name: &'static str,
81//! surname: &'static str,
82//! }
83//!
84//! impl OtherPerson {
85//! pub fn new(name: &'static str, surname: &'static str) -> Self {
86//! Self{name, surname}
87//! }
88//! }
89//!
90//! impl Index<TS!(name)> for OtherPerson {
91//! type Output = str;
92//!
93//! fn index(&self, _: TS!(name)) -> &str {
94//! self.name
95//! }
96//! }
97//!
98//! impl Index<TS!(surname)> for OtherPerson {
99//! type Output = str;
100//!
101//! fn index(&self, _: TS!(surname)) -> &str {
102//! self.surname
103//! }
104//! }
105//! }
106//!
107//! ```
108//!
109//! ### Type errors
110//!
111//! This example showcases what TStr looks like in simple type errors.
112//!
113//! ```rust,compile_fail
114//! let _: tstr::TS!("Hello, world!") = ();
115//! ```
116//!
117//! With no crate features enabled, the error message is this:
118//! ```text
119//! error[E0308]: mismatched types
120//! --> tstr/src/lib.rs:114:37
121//! |
122//! 5 | let _: tstr::TS!("Hello, world!") = ();
123//! | -------------------------- ^^ expected `TStr<___<..., 13>>`, found `()`
124//! | |
125//! | expected due to this
126//! |
127//! = note: expected struct `tstr::TStr<___<(tstr::__<'H', 'e', 'l', 'l', 'o', ',', ' ', 'w'>, tstr::__<'o', 'r', 'l', 'd', '!'>, (), (), (), (), (), ()), 13>>`
128//! found unit type `()`
129//! ```
130//! As you can see, the string is represented as a collection of `char` const parameters.
131//!
132//! When the `"nightly_str_generics"` feature is enabled (which requires the nightly compiler),
133//! the error message is this:
134//! ```text
135//! error[E0308]: mismatched types
136//! --> tstr/src/lib.rs:114:37
137//! |
138//! 5 | let _: tstr::TS!("Hello, world!") = ();
139//! | -------------------------- ^^ expected `TStr<___<"Hello, world!">>`, found `()`
140//! | |
141//! | expected due to this
142//! |
143//! = note: expected struct `tstr::TStr<___<"Hello, world!">>`
144//! found unit type `()`
145//! ```
146//!
147//!
148//!
149//! # Macro expansion
150//!
151//! This library reserves the right to change how it represent type-level strings internally
152//! in every single release, and cargo feature combination.
153//!
154//! This only affects you if you expand the code generated by macros from this crate,
155//! and then use that expanded code instead of going through the macros.
156//!
157//! # Cargo features
158//!
159//! - `"const_panic"`(enabled by default):
160//! Enables [`const_panic`] reexports, assertion macros,
161//! and `const_panic::fmt::PanicFmt` impl for `TStr`.
162//!
163//! - `"use_syn"`(disabled by default):
164//! Changes how literals passed to the macros of this crate are parsed to use the `syn` crate.
165//! Use this if there is some literal that could not be
166//! parsed but is a valid str/integer literal.
167//!
168//! - `"serde"`(disabled by default):
169//! Enables serde dependency and implements `serde::{Serialize, Deserialize}` for `TStr`
170//!
171//! - `"str_generics"`(disabled by default):
172//! Changes the representation of type-level strings to use a `&'static str` const parameter,
173//! making for better compiler errors.
174//! As of 2025-08-18, this feature can't be enabled, because it
175//! requires `&'static str` to be stably usable as const parameters.
176//! Consider using `"nightly_str_generics"` if this feature can't be used.
177//!
178//! - `"nightly_str_generics"`(disabled by default): Equivalent to the `"str_generics"` feature,
179//! and enables the nightly compiler features to use `&'static str` const parameters.
180//!
181//! # No-std support
182//!
183//! This crate is unconditionally `#![no_std]`, and can be used anywhere that Rust can be.
184//!
185//! # Minimum Supported Rust Version
186//!
187//! This crate supports Rust versions back to Rust 1.88.0.
188//!
189//! [`TStr`]: crate::TStr
190//! [`const_panic`]: const_panic
191//! [`Index`]: core::ops::Index
192//! [`tstr::utils`]: crate::utils
193#![no_std]
194#![cfg_attr(feature = "nightly_str_generics", feature(adt_const_params))]
195#![cfg_attr(feature = "nightly_str_generics", feature(unsized_const_params))]
196#![cfg_attr(feature = "docsrs", feature(doc_cfg))]
197#![cfg_attr(feature = "nightly_str_generics", allow(incomplete_features))]
198//////////
199// lints
200//////////
201#![allow(non_camel_case_types)]
202#![forbid(unsafe_code)]
203
204#[cfg(feature = "const_panic")]
205mod assertions;
206
207pub mod strlike;
208
209mod macros;
210mod private_macros;
211
212mod tstr_fns;
213mod tstr_trait;
214mod tstr_type;
215
216#[cfg(feature = "serde")]
217mod tstr_serde_impls;
218
219#[cfg(not(feature = "str_generics"))]
220mod tstr_impl_with_chars;
221
222#[cfg(not(feature = "str_generics"))]
223pub(crate) use tstr_impl_with_chars::__TStrRepr;
224
225#[cfg(feature = "str_generics")]
226mod tstr_impl_with_str;
227
228#[cfg(feature = "str_generics")]
229pub(crate) use tstr_impl_with_str::__TStrRepr;
230
231pub mod utils;
232
233#[doc(hidden)]
234extern crate self as tstr;
235
236#[doc(hidden)]
237pub use tstr_proc_macros::__ts_impl;
238
239use crate::tstr_trait::__TStrArgBinary;
240
241pub use crate::{
242 tstr_fns::*,
243 tstr_trait::{IsTStr, TStrArg},
244 tstr_type::TStr,
245};
246
247pub use typewit;
248
249#[doc(no_inline)]
250#[cfg(feature = "const_panic")]
251#[cfg_attr(feature = "docsrs", doc(cfg(feature = "const_panic")))]
252pub use const_panic::{self, unwrap_ok as unwrap};
253
254include! {"./p.rs"}
255
256#[doc(hidden)]
257pub mod __p {
258 pub use crate::__Empty;
259 pub use crate::macros::__IgnoreArgReturnEmpty;
260
261 #[cfg(feature = "const_panic")]
262 pub use const_panic::concat_panic;
263
264 pub use core::array;
265 pub use core::{concat, stringify};
266}