arcstr/mac.rs
1/// Create a const [`ArcStr`](crate::ArcStr) from a string literal. The
2/// resulting `ArcStr` require no heap allocation, can be freely cloned and used
3/// interchangeably with `ArcStr`s from the heap, and are effectively "free".
4///
5/// The main downside is that it's a macro. Eventually it may be doable as a
6/// `const fn`, which would be cleaner, but for now the drawbacks to this are
7/// not overwhelming, and the functionality it provides is very useful.
8///
9/// # Usage
10///
11/// ```
12/// # use arcstr::ArcStr;
13/// // Works in const:
14/// const MY_ARCSTR: ArcStr = arcstr::literal!("testing testing");
15/// assert_eq!(MY_ARCSTR, "testing testing");
16///
17/// // Or, just in normal expressions.
18/// assert_eq!("Wow!", arcstr::literal!("Wow!"));
19/// ```
20///
21/// Another motivating use case is bundled files:
22///
23/// ```rust,ignore
24/// use arcstr::ArcStr;
25/// const VERY_IMPORTANT_FILE: ArcStr =
26/// arcstr::literal!(include_str!("./very-important.txt"));
27/// ```
28#[macro_export]
29macro_rules! literal {
30 ($text:expr $(,)?) => {{
31 // Note: extra scope to reduce the size of what's in `$text`'s scope
32 // (note that consts in macros dont have hygene the way let does).
33 const __TEXT: &$crate::_private::str = $text;
34 {
35 #[allow(clippy::declare_interior_mutable_const)]
36 const SI: &$crate::_private::StaticArcStrInner<[$crate::_private::u8; __TEXT.len()]> = unsafe {
37 &$crate::_private::StaticArcStrInner {
38 len_flag: match $crate::_private::StaticArcStrInner::<[$crate::_private::u8; __TEXT.len()]>::encode_len(__TEXT.len()) {
39 Some(len) => len,
40 None => $crate::core::panic!("impossibly long length")
41 },
42 count_flag: $crate::_private::StaticArcStrInner::<[$crate::_private::u8; __TEXT.len()]>::STATIC_COUNT_VALUE,
43 // See comment for `_private::ConstPtrDeref` for what the hell's
44 // going on here.
45 data: *$crate::_private::ConstPtrDeref::<[$crate::_private::u8; __TEXT.len()]> {
46 p: __TEXT.as_ptr(),
47 }
48 .a,
49 // data: __TEXT.as_ptr().cast::<[$crate::_private::u8; __TEXT.len()]>().read(),
50 }
51 };
52 #[allow(clippy::declare_interior_mutable_const)]
53 const S: $crate::ArcStr = unsafe { $crate::ArcStr::_private_new_from_static_data(SI) };
54 S
55 }
56 }};
57}
58
59/// Conceptually equivalent to `ArcStr::from(format!("...", args...))`.
60///
61/// In the future, this will be implemented in such a way to avoid an additional
62/// string copy which is required by the `from` operation.
63///
64/// # Example
65///
66/// ```
67/// let arcstr = arcstr::format!("testing {}", 123);
68/// assert_eq!(arcstr, "testing 123");
69/// ```
70#[macro_export]
71macro_rules! format {
72 ($($toks:tt)*) => {
73 $crate::ArcStr::from($crate::alloc::fmt::format($crate::core::format_args!($($toks)*)))
74 };
75}
76
77/// `feature = "substr"`: Create a `const` [`Substr`][crate::Substr].
78///
79/// This is a wrapper that initializes a `Substr` over the entire contents of a
80/// `const` [`ArcStr`](crate::ArcStr) made using [arcstr::literal!](crate::literal).
81///
82/// As with `arcstr::literal`, these require no heap allocation, can be freely
83/// cloned and used interchangeably with `ArcStr`s from the heap, and are
84/// effectively "free".
85///
86/// The main use case here is in applications where `Substr` is a much more
87/// common string type than `ArcStr`.
88///
89/// # Examples
90///
91/// ```
92/// use arcstr::{Substr, literal_substr};
93/// // Works in const:
94/// const EXAMPLE_SUBSTR: Substr = literal_substr!("testing testing");
95/// assert_eq!(EXAMPLE_SUBSTR, "testing testing");
96///
97/// // Or, just in normal expressions.
98/// assert_eq!("Wow!", literal_substr!("Wow!"));
99/// ```
100#[macro_export]
101#[cfg(feature = "substr")]
102macro_rules! literal_substr {
103 ($text:expr $(,)?) => {{
104 const __S: &$crate::_private::str = $text;
105 {
106 const PARENT: $crate::ArcStr = $crate::literal!(__S);
107 const SUBSTR: $crate::Substr =
108 unsafe { $crate::Substr::from_parts_unchecked(PARENT, 0..__S.len()) };
109 SUBSTR
110 }
111 }};
112}
113
114#[cfg(test)]
115mod test {
116 #[test]
117 fn ensure_no_import() {
118 let v = literal!("foo");
119 assert_eq!(v, "foo");
120 #[cfg(feature = "substr")]
121 {
122 let substr = literal_substr!("bar");
123 assert_eq!(substr, "bar");
124 }
125 // Loom doesn't like it if you do things outside `loom::model`, AFAICT.
126 // These calls produce error messages from inside `libstd` about
127 // accessing thread_locals that haven't been initialized.
128 #[cfg(not(loom))]
129 {
130 let test = crate::format!("foo");
131 assert_eq!(test, "foo");
132 let test2 = crate::format!("foo {}", 123);
133 assert_eq!(test2, "foo 123");
134 #[cfg(not(msrv))]
135 {
136 let foo = "abc";
137 let test3 = crate::format!("foo {foo}");
138 assert_eq!(test3, "foo abc");
139 }
140 }
141 }
142}