stringz/lib.rs
1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![deny(missing_docs)]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5//! Convert strings to types to make it available as generic parameters.
6//!
7//! # Install
8//!
9//! ```bash
10//! cargo add stringz
11//! ```
12//!
13//! For `no_std` users:
14//!
15//! ```bash
16//! cargo add stringz --no-default-features
17//! ```
18//!
19//! # Example
20//!
21//! ```
22//! use stringz::{TypedString, string};
23//!
24//! fn test_hello<T: TypedString>() {
25//! assert_eq!(T::value(), "hello");
26//! }
27//!
28//! test_hello::<string!("hello")>();
29//! ```
30//!
31//! # Explanation
32//!
33//! The [`string`] macro converts `"hello"` to the following tuple type:
34//!
35//! ```text
36//! (Character<'h'>, Character<'e'>, Character<'l'>, Character<'l'>, Character<'o'>)
37//! ```
38//!
39//! Note: The above form is only for ease of understanding, the actual [`Tuple`] type of
40//! [tuplez](https://docs.rs/tuplez) is used.
41//!
42//! All generated types are zero-sized types:
43//!
44//! ```
45//! use stringz::string;
46//! assert_eq!(std::mem::size_of::<string!("no matter how long it is")>(), 0);
47//! ```
48
49extern crate self as stringz;
50
51#[doc(hidden)]
52pub use stringz_macros::{ident as ident_inner, string as string_inner};
53#[doc(hidden)]
54pub use tuplez as __tuplez;
55
56/// Convert a string to a type, the input must be a string literal.
57///
58/// # Example
59///
60/// ```
61/// use stringz::{TypedString, string};
62///
63/// fn test_hello<T: TypedString>() {
64/// assert_eq!(T::value(), "hello");
65/// }
66///
67/// test_hello::<string!("hello")>();
68/// ```
69#[macro_export]
70macro_rules! string {
71 ($s:literal) => {
72 $crate::string_inner!($crate; $s)
73 };
74}
75
76/// Convert a string to a type, the input must be an identifier.
77///
78/// # Example
79///
80/// ```
81/// use stringz::{TypedString, ident};
82///
83/// fn test_hello<T: TypedString>() {
84/// assert_eq!(T::value(), "hello");
85/// }
86///
87/// test_hello::<ident!(hello)>();
88/// ```
89#[macro_export]
90macro_rules! ident {
91 ($i:ident) => {
92 $crate::ident_inner!($crate; $i)
93 };
94}
95
96#[cfg(all(not(feature = "std"), feature = "alloc"))]
97extern crate alloc;
98
99#[cfg(all(not(feature = "std"), feature = "alloc"))]
100use alloc::{
101 format,
102 string::{String, ToString},
103};
104
105/// Single `char` type value representation.
106#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
107pub struct Character<const C: char>;
108
109/// Get original string from typed string (requires `alloc` or `std` feature).
110pub trait TypedString {
111 /// The original string.
112 #[cfg(any(feature = "std", feature = "alloc"))]
113 #[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
114 fn value() -> String;
115}
116
117impl<const C: char> TypedString for __tuplez::Tuple<Character<C>, __tuplez::Unit> {
118 #[cfg(any(feature = "std", feature = "alloc"))]
119 fn value() -> String {
120 C.to_string()
121 }
122}
123
124impl<const C: char, Other> TypedString for __tuplez::Tuple<Character<C>, Other>
125where
126 Other: TypedString,
127{
128 #[cfg(any(feature = "std", feature = "alloc"))]
129 fn value() -> String {
130 format!("{}{}", C, Other::value())
131 }
132}
133
134/// Concatenate multiple typed strings.
135///
136/// # Example
137///
138/// ```
139/// use stringz::*;
140///
141/// type CrateName = ident!(my_crate);
142/// type ModPath = string!("foo::bar");
143/// type StructName = ident!(Amazing);
144/// type FullQualified = concatstr!(CrateName, string!("::"), ModPath, string!("::"), StructName);
145///
146/// fn test_type() -> string!("my_crate::foo::bar::Amazing") {
147/// FullQualified::default()
148/// }
149/// ```
150#[macro_export]
151macro_rules! concatstr {
152 ($t:ty) => {
153 $t
154 };
155 ($t:ty, $($ts:ty),*) => {
156 <$t as $crate::__tuplez::TupleLike>::JoinOutput<$crate::concatstr!($($ts),*)>
157 };
158}