stringlet/
lib.rs

1#![doc = include_str!("../README.md")]
2#![deny(clippy::alloc_instead_of_core)]
3#![deny(clippy::std_instead_of_core)]
4/* #![no_std]
5extern crate alloc; */
6
7mod cmp;
8mod fmt;
9mod macros;
10mod methods;
11mod new;
12mod refs;
13mod traits;
14
15/// Configure `StringletBase` to have only valid generic parameters.
16#[diagnostic::on_unimplemented(
17    message = "`VarStringlet<{SIZE}>` or `SlimStringlet<{SIZE}>` has excessive SIZE",
18    label = "SIZE must be `0..=255` or `0..=64`",
19    note = "`VarStringlet` cannot be longer than 255 bytes. Consider using `String`!",
20    note = "`SlimStringlet` cannot be longer than 64 bytes. Consider using `VarStringlet`!"
21)]
22#[doc(hidden)]
23pub trait Config<Kind, const SIZE: usize, const LEN: usize = 0, const ALIGN: u8 = 1> {
24    type Aligned: Copy + Eq + Ord;
25    const ABBR: u8;
26}
27
28// Emulate enum, which generic can’t handle yet. No need for trait, as they’re constrained by Config.
29#[derive(Copy, Clone)]
30pub struct Fixed;
31#[derive(Copy, Clone)]
32pub struct Trim;
33#[derive(Copy, Clone)]
34pub struct Var;
35#[derive(Copy, Clone)]
36pub struct Slim;
37
38macro_rules! config {
39    ($kind:ident $msg:literal: $stringlet:ident, $aligned:ident, $len:literal, 1) => {
40        #[doc = concat!($msg, " length Stringlet")]
41        pub type $stringlet<const SIZE: usize = 16> =
42            StringletBase<$kind, SIZE, $len, 1>;
43    };
44    ($kind:ident $msg:literal: $stringlet:ident, $aligned:ident, $len:literal, $align:literal) => {
45        #[doc = concat!($msg, " length Stringlet, aligned to ", $align, " bytes")]
46        pub type $stringlet<const SIZE: usize = 16> =
47            StringletBase<$kind, SIZE, $len, $align>;
48    };
49    ($($stringlet:ident, $trim_stringlet:ident, $var_stringlet:ident, $slim_stringlet:ident: $aligned:ident @ $align:literal;)+) => {
50        $(
51            #[doc(hidden)]
52            #[repr(align($align))]
53            #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
54            pub struct $aligned;
55        )+
56        $(
57            config!(Fixed "Fixed": $stringlet, $aligned, 0, $align);
58            config!(Trim "Trimmed": $trim_stringlet, $aligned, 0, $align);
59            config!(Var "Variable": $var_stringlet, $aligned, 1, $align);
60            config!(Slim "Slim variable": $slim_stringlet, $aligned, 0, $align);
61
62            // todo Is there an easier way to configure all valid sizes?
63            config![ // for VarStringlet and SlimStringlet
64                0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
65                26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
66                50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64: $aligned @ $align
67            ];
68            config![ // for VarStringlet only
69                + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
70                88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
71                109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
72                128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
73                147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
74                166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184,
75                185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203,
76                204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222,
77                223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241,
78                242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255: Var $aligned @ 1, $align
79            ];
80        )+
81    };
82    ($($size:tt),+: $aligned:ident @ $align:literal) => {
83        /* impl<const SIZE: usize> Config<Fixed, SIZE, 0, $align>
84            for StringletBase<Fixed, SIZE, 0, $align> { type Aligned = $aligned; }
85        impl<const SIZE: usize> Config<Trim, SIZE, 0, $align>
86            for StringletBase<Trim, SIZE, 0, $align> { type Aligned = $aligned; } */
87        config![+ SIZE: Fixed $aligned @ $align];
88        config![+ SIZE: Trim $aligned @ $align];
89        config![+ $($size),+: Var $aligned @ 1, $align];
90        config![+ $($size),+: Slim $aligned @ 0, $align];
91    };
92    (+ SIZE: $kind:ident $aligned:ident @ $align:literal) => {
93        impl<const SIZE: usize> Config<$kind, SIZE, 0, $align>
94            for StringletBase<$kind, SIZE, 0, $align>
95        {
96            type Aligned = $aligned;
97            const ABBR: u8 = stringify!($kind).as_bytes()[0];
98        }
99    };
100    (+ $($size:tt),+: $kind:ident $aligned:ident @ $len:literal, $align:literal) => {
101        $(
102            impl Config<$kind, $size, $len, $align>
103                for StringletBase<$kind, $size, $len, $align>
104            {
105                type Aligned = $aligned;
106                const ABBR: u8 = stringify!($kind).as_bytes()[0];
107            }
108        )+
109    };
110}
111
112config! {
113    Stringlet,   TrimStringlet,   VarStringlet,   SlimStringlet:   Align1  @  1;
114    Stringlet2,  TrimStringlet2,  VarStringlet2,  SlimStringlet2:  Align2  @  2;
115    Stringlet4,  TrimStringlet4,  VarStringlet4,  SlimStringlet4:  Align4  @  4;
116    Stringlet8,  TrimStringlet8,  VarStringlet8,  SlimStringlet8:  Align8  @  8;
117    Stringlet16, TrimStringlet16, VarStringlet16, SlimStringlet16: Align16 @ 16;
118    Stringlet32, TrimStringlet32, VarStringlet32, SlimStringlet32: Align32 @ 32;
119    Stringlet64, TrimStringlet64, VarStringlet64, SlimStringlet64: Align64 @ 64;
120}
121
122/// An inline String of varying size bounds, which can be handled like a primitive type.
123#[derive(Copy, Clone)]
124pub struct StringletBase<
125    Kind,
126    const SIZE: usize,
127    // Have to make LEN explicit, as (unlike type Aligned) we can’t pick up a const from Config.
128    // We could put the whole [u8; LEN] into an associated type, but then it would be opaque to us.
129    const LEN: usize = 0,
130    const ALIGN: u8 = 1,
131> where
132    Self: Config<Kind, SIZE, LEN, ALIGN>,
133{
134    /// Zero size type that is aligned according to `ALIGN` and never touched.
135    pub(crate) _align: [<Self as Config<Kind, SIZE, LEN, ALIGN>>::Aligned; 0],
136    /// The actual payload, if it is shorter than SIZE, either LEN == 1 or its last bytes will be tagged.
137    pub(crate) str: [u8; SIZE],
138    /// Limited by `Config<SIZE, Self::FIXED, LEN, ALIGN>` to either 0 or 1 byte for an explicit length.
139    // str can’t be SIZE + LEN, as “generic parameters may not be used in const operations”
140    pub(crate) len: [u8; LEN],
141}
142
143/// A 2<sup>nd</sup> generic StringletBase
144macro_rules! self2 {
145    () => {
146        StringletBase<Kind2, SIZE2, LEN2, ALIGN2>
147    };
148}
149
150/** Impl `SomeTrait` for `Self`, hiding repeated boilerplate caused by lack of nested impls.
151```ignore
152impl_for! { SomeTrait }
153impl_for! { SomeTrait: impl_body }
154impl_for! { <'a, 2> SomeTrait<self2!()>: impl_body }
155```
156where `'a`, `2` and impl_body are all optional, `2` meaning 2<sup>nd</sup> generic Stringlet.
157*/
158macro_rules! impl_for {
159    // Split this rule, otherwise compiler says optional <…> is ambiguous
160    // $two should match nothing, but marks that 2 was matched.
161    (<$($lt:lifetime)? $(,)? $(2 $($two:literal)?)?> $trait:ty $(: $($rest:tt)+)?) => {
162        impl_for!(@ $(2 $($two)?)? $($lt)?; $trait: $($($rest)+)?);
163    };
164    ($trait:ty $(: $($rest:tt)+)?) => {
165        impl_for!(@ ; $trait: $($($rest)+)?);
166    };
167
168    (@ $(2 $($two:literal)?)? $($lt:lifetime)?; $trait:ty: $($rest:tt)*) => {
169        impl_for!(@@
170            $(2 $($two)?)?
171            $($lt)?
172            [Kind, const SIZE: usize, const LEN: usize, const ALIGN: u8,]
173            [StringletBase<Kind, SIZE, LEN, ALIGN>: Config<Kind, SIZE, LEN, ALIGN>,]
174            $trait:
175            $($rest)*
176        );
177    };
178    (@@ 2 $($lt:lifetime)? [$($gen:tt)+] [$($where:tt)+] $trait:ty: $($rest:tt)*) => {
179        impl_for!(@@
180            $($lt)?
181            [$($gen)+ Kind2, const SIZE2: usize, const LEN2: usize, const ALIGN2: u8]
182            [$($where)+ self2!(): Config<Kind2, SIZE2, LEN2, ALIGN2>,]
183            $trait:
184            $($rest)*
185        );
186    };
187    (@@ $($lt:lifetime)? [$($gen:tt)+] [$($where:tt)+] $trait:ty: $($rest:tt)*) => {
188        impl<$($lt,)? $($gen)+> $trait
189        for StringletBase<Kind, SIZE, LEN, ALIGN>
190        where $($where)+
191        {
192            $($rest)*
193        }
194    };
195}
196
197pub(crate) use impl_for;
198pub(crate) use self2;