str_array/lib.rs
1//! Provides fixed-size string types [`StrArray<N>`] and [`CStrArray<N>`].
2//!
3//! [`StrArray`] serves as the `str` equivalent of `[u8; N]`.
4//! It provides a `Deref` to `&str` and ensures the UTF-8 invariant is
5//! always upheld, but has a size known at compile time.
6//!
7//! This is useful in some situations:
8//!
9//! - Resource-constrained `no_std` and no-`alloc` environments.
10//! - Defining UTF-8 strings directly in a stack array or static.
11//! - Types or parameters that require `&str` of some fixed length.
12//!
13//! The [`str_array!`] macro provides a compile-time-checked way to
14//! build [`StrArray`] values from string literals and constants.
15//!
16//! Similarly, [`CStrArray`] and [`cstr_array!`] can construct a
17//! nul-terminated [`CStr`](core::ffi::CStr) safely on the stack.
18//!
19//! # Features
20//!
21//! - `no_std` support - disable default features to use without `std`
22//! - Optional `alloc` and `std` features
23//! - Full `const` support
24//! - [C string](crate::CStrArray) support
25//!
26//! # Examples
27//!
28//! ```
29//! use str_array::{str_array, StrArray};
30//!
31//! // Create from a constant using the macro. The length is inferred.
32//! let s1 = str_array!("hello");
33//! assert_eq!(s1.len(), 5);
34//! assert_eq!(s1, "hello");
35//! assert!(matches!(s1.into_bytes(), [b'h', b'e', b'l', b'l', b'o']));
36//!
37//! // Or create from a runtime &str with an length check.
38//! let s2: StrArray<12> = StrArray::new(&format!("{s1}, world")).unwrap();
39//! assert_eq!(core::mem::size_of_val(&s2), 12);
40//! assert_eq!(s2, "hello, world");
41//!
42//! // Or create from bytes with a UTF-8 check.
43//! let s3 = StrArray::from_utf8(
44//! b"\xF0\x9F\xA4\x8C\xF0\x9F\x8F\xBC"
45//! ).unwrap();
46//! assert_eq!(s3, "🤌🏼");
47//!
48//! // Or define an item with an inferred length.
49//! str_array! {
50//! static S4 = "Georgia";
51//! }
52//! assert_eq!(S4.len(), 7);
53//! ```
54//!
55//! # Rust feature detection
56//!
57//! This crate has a low Minimum Supported Rust Version (MSRV), and it achieves
58//! that through `cfg` values that are enabled in the `build.rs` based on the
59//! presence of existing features. This uses the `autocfg` library.
60//!
61//! The `build.rs` can be skipped for alternate build systems, but be sure to
62//! enable the appropriate `cfg` values based on your version of `rustc`.
63//! Those are:
64//!
65//! - `cfg(has_core_error)` is enabled when `core::error::Error` is present
66//! (stable since Rust 1.81).
67//! If it's enabled, then the error types in this crate implement `Error`
68//! with any set of features. If it's disabled, they only implement the
69//! `Error` trait when the `std` feature is enabled.
70//! - `cfg(has_const_mut)` is enabled when `&mut` is usable in `const`
71//! (stable since Rust 1.83).
72//! It adds `const` to various `fn` that use `&mut` in this crate.
73#![no_std]
74#![deny(missing_docs, unsafe_op_in_unsafe_fn)]
75#![deny(rustdoc::broken_intra_doc_links)]
76
77#[cfg(any(test, feature = "alloc"))]
78extern crate alloc;
79#[cfg(any(test, feature = "std"))]
80extern crate std;
81
82use core::{
83 fmt::{self, Debug, Display},
84 ops::{Deref, DerefMut},
85 str::Utf8Error,
86};
87
88mod cmp;
89mod convert;
90mod cstr;
91pub mod error;
92
93mod util_macros;
94
95use error::StrLenError;
96use util_macros::const_mut_fn;
97
98pub use cstr::CStrArray;
99
100/// Internal-only items which may change with a non-breaking release.
101#[doc(hidden)]
102pub mod __internal {
103 pub use crate::cstr::{build_cstr, CStrArrayBytes};
104}
105
106/// Fixed-size [`str`], backed by an array.
107///
108/// `[u8; N]` is to `[u8]` as `StrArray<N>` is to `str`.
109/// It provides a `Deref` to `&str` and ensures the UTF-8 invariant is
110/// always upheld, but has a size known at compile time.
111///
112/// [`str_array!`] provides a convenient way to construct
113/// and define `StrArray` from literals and constants.
114///
115/// # Examples
116///
117/// Small UTF-8 strings of fixed size:
118///
119/// ```
120/// # use str_array::str_array;
121/// let mut airports = [
122/// str_array!("JFK"), str_array!("LAX"),
123/// str_array!("LHR"), str_array!("CDG"),
124/// str_array!("HND"), str_array!("PEK"),
125/// str_array!("DXB"), str_array!("AMS"),
126/// str_array!("FRA"), str_array!("SIN"),
127/// ];
128///
129/// // All of the strings are contiguous in memory.
130/// assert_eq!(core::mem::size_of_val(&airports), 30);
131///
132/// airports.sort();
133/// assert_eq!(airports[0], "AMS");
134/// ```
135///
136/// Storing `str` contents directly in a `static`:
137///
138/// ```
139/// # use core::mem::size_of_val;
140/// # use str_array::str_array;
141/// str_array! {
142/// static FOO = "Hello, world!";
143/// static mut FOO_MUT = "utf-8 buffer";
144/// }
145/// assert_eq!(&FOO, "Hello, world!");
146/// assert_eq!(size_of_val(&FOO), 13);
147/// let foo_mut = unsafe { &mut *&raw mut FOO_MUT };
148/// foo_mut.make_ascii_uppercase();
149/// assert_eq!(foo_mut, "UTF-8 BUFFER");
150/// assert_eq!(size_of_val(foo_mut), 12);
151/// ```
152#[repr(transparent)]
153#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
154pub struct StrArray<const N: usize>(
155 /// # Safety
156 /// Must be UTF-8 encoded bytes.
157 [u8; N],
158);
159
160impl<const N: usize> Debug for StrArray<N> {
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 write!(f, "StrArray<{N}>({s:?})", s = self.as_str())
163 }
164}
165
166impl<const N: usize> Display for StrArray<N> {
167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168 <str as Display>::fmt(self.as_str(), f)
169 }
170}
171
172impl<const N: usize> StrArray<N> {
173 /// Builds a `StrArray<N>` from `val` by performing a length check.
174 ///
175 /// This returns an `Err` if `val.len() != N`.
176 ///
177 /// If `val` is a literal or `const`, consider using [`str_array!`]
178 /// instead, which always builds a `StrArray` with the correct `N`.
179 ///
180 /// # Examples
181 ///
182 /// ```
183 /// # use str_array::StrArray;
184 /// let s = StrArray::<5>::new(&format!("he{}", "llo")).unwrap();
185 /// assert_eq!(s, "hello");
186 /// assert!(StrArray::<5>::new("foo").is_err());
187 /// ```
188 pub const fn new(val: &str) -> Result<Self, StrLenError<N>> {
189 match Self::ref_from_str(val) {
190 Ok(out) => Ok(*out),
191 Err(e) => Err(e),
192 }
193 }
194
195 /// Builds a `StrArray<N>` from `val` without a length check.
196 ///
197 /// # Examples
198 ///
199 /// ```
200 /// # use str_array::StrArray;
201 /// // SAFETY: "hello" has length 5
202 /// let s = unsafe { StrArray::<5>::new_unchecked("hello") };
203 /// assert_eq!(s, "hello");
204 /// ```
205 ///
206 /// # Safety
207 ///
208 /// `val.len() >= N` or else behavior is undefined.
209 pub const unsafe fn new_unchecked(val: &str) -> Self {
210 // SAFETY: `val` has at least size `N` as promised by the caller.
211 Self(unsafe { *(val.as_bytes() as *const [u8]).cast() })
212 }
213
214 /// Converts a `&str` to `&StrArray<N>` without copying.
215 ///
216 /// Returns an `Err` if `val.len() != N`.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// # use str_array::StrArray;
222 /// let s = StrArray::<5>::ref_from_str("hello").unwrap();
223 /// assert_eq!(s, "hello");
224 ///
225 /// assert!(StrArray::<5>::ref_from_str("foo").is_err());
226 /// ```
227 pub const fn ref_from_str(val: &str) -> Result<&Self, StrLenError<N>> {
228 if val.len() != N {
229 return Err(StrLenError { src_len: val.len() });
230 }
231 // SAFETY: val.len() == N.
232 Ok(unsafe { Self::ref_from_str_unchecked(val) })
233 }
234
235 /// Converts a `&str` to `&StrArray<N>` without copying and no length check.
236 ///
237 /// # Examples
238 ///
239 /// ```
240 /// # use str_array::StrArray;
241 /// // SAFETY: "abc".len() == 3
242 /// let s: &StrArray<3> = unsafe {
243 /// StrArray::ref_from_str_unchecked("abc")
244 /// };
245 /// assert_eq!(s.into_bytes(), [b'a', b'b', b'c']);
246 /// ```
247 ///
248 /// # Safety
249 ///
250 /// `val.len() == N` or else behavior is undefined.
251 pub const unsafe fn ref_from_str_unchecked(val: &str) -> &Self {
252 // SAFETY:
253 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`, as is `str`.
254 // - The caller has promised that `*val` has the same size as `[u8; N]`.
255 // - `val` is UTF-8, matching the requirement of `Self::0`.
256 unsafe { &*val.as_ptr().cast() }
257 }
258
259 const_mut_fn! {
260 /// Converts a `&mut str` to `&mut StrArray<N>` without copying.
261 ///
262 /// This returns an `Err` if `val.len() != N`.
263 ///
264 /// # Examples
265 ///
266 /// ```
267 /// # use str_array::StrArray;
268 /// let mut s = String::from("hello");
269 /// let s_mut = StrArray::<5>::mut_from_str(&mut s).unwrap();
270 /// s_mut.make_ascii_uppercase();
271 /// assert_eq!(s, "HELLO");
272 ///
273 /// assert!(StrArray::<5>::mut_from_str(&mut String::from("foo")).is_err());
274 /// ```
275 pub fn mut_from_str(val: &mut str) -> Result<&mut Self, StrLenError<N>> {
276 if val.len() != N {
277 return Err(StrLenError {
278 src_len: val.len(),
279 });
280 }
281 // SAFETY: val.len() == N.
282 Ok(unsafe { Self::mut_from_str_unchecked(val) })
283 }
284 }
285
286 const_mut_fn! {
287 /// Converts a `&mut str` to `&mut StrArray<N>` without copying and no length check.
288 ///
289 /// # Examples
290 ///
291 /// ```
292 /// # use str_array::StrArray;
293 /// let mut s = String::from("abc");
294 ///
295 /// // SAFETY: `s.len() == 3`
296 /// let m: &mut StrArray<3> = unsafe {
297 /// StrArray::mut_from_str_unchecked(&mut s)
298 /// };
299 /// m.make_ascii_uppercase();
300 /// assert_eq!(s, "ABC");
301 /// ```
302 ///
303 /// # Safety
304 ///
305 /// `val.len() == N` or else behavior is undefined.
306 pub unsafe fn mut_from_str_unchecked(val: &mut str) -> &mut Self {
307 // SAFETY:
308 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`, as is `str`.
309 // - The caller has promised that `*val` has the same size as `[u8; N]`.
310 // - `val` is UTF-8.
311 unsafe { &mut *val.as_mut_ptr().cast() }
312 }
313 }
314
315 /// Copies UTF-8 bytes from `val` into a `StrArray<N>`.
316 ///
317 /// # Examples
318 ///
319 /// ```
320 /// # use str_array::StrArray;
321 /// let s = StrArray::<5>::from_utf8(b"hello").unwrap();
322 /// assert_eq!(s, "hello");
323 /// ```
324 pub const fn from_utf8(val: &[u8; N]) -> Result<Self, Utf8Error> {
325 match Self::ref_from_utf8(val) {
326 Ok(&a) => Ok(a),
327 Err(e) => Err(e),
328 }
329 }
330
331 /// Copies UTF-8 bytes from `val` into a `StrArray<N>` without a UTF-8 check.
332 ///
333 /// # Examples
334 ///
335 /// ```
336 /// # use str_array::StrArray;
337 /// // SAFETY: "hello" is valid UTF-8.
338 /// let s = unsafe { StrArray::<5>::from_utf8_unchecked(b"hello") };
339 /// assert_eq!(s, "hello");
340 /// ```
341 ///
342 /// # Safety
343 ///
344 /// `val` must contain valid UTF-8 or behavior is undefined.
345 pub const unsafe fn from_utf8_unchecked(val: &[u8; N]) -> Self {
346 // SAFETY: The caller guarantees `val` is valid UTF-8.
347 Self(unsafe { *(val as *const [u8; N]).cast() })
348 }
349
350 /// Converts UTF-8 bytes in `val` to `&StrArray<N>` without copying.
351 ///
352 /// # Examples
353 ///
354 /// ```
355 /// # use str_array::StrArray;
356 /// let s = StrArray::<5>::ref_from_utf8(b"hello").unwrap();
357 /// assert_eq!(s, "hello");
358 /// ```
359 pub const fn ref_from_utf8(val: &[u8; N]) -> Result<&Self, Utf8Error> {
360 match core::str::from_utf8(val) {
361 // SAFETY:
362 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`.
363 // - `val` is valid UTF-8.
364 Ok(_) => Ok(unsafe { &*(val as *const [u8; N]).cast() }),
365 Err(e) => Err(e),
366 }
367 }
368
369 /// Converts UTF-8 bytes in `val` to `&StrArray<N>` without a UTF-8 check.
370 ///
371 /// # Examples
372 ///
373 /// ```
374 /// # use str_array::StrArray;
375 /// // SAFETY: "hello" is valid UTF-8
376 /// let s: &StrArray<5> = unsafe { StrArray::ref_from_utf8_unchecked(b"hello") };
377 /// assert_eq!(s, "hello");
378 /// ```
379 ///
380 /// # Safety
381 ///
382 /// `val` must contain valid UTF-8 or behavior is undefined.
383 pub const unsafe fn ref_from_utf8_unchecked(val: &[u8; N]) -> &Self {
384 // SAFETY:
385 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`.
386 // - The caller guarantees `val` is valid UTF-8.
387 unsafe { &*(val as *const [u8; N]).cast() }
388 }
389
390 const_mut_fn! {
391 /// Converts UTF-8 bytes in `val` to `&mut StrArray<N>` without copying.
392 ///
393 /// # Examples
394 ///
395 /// ```
396 /// # use str_array::StrArray;
397 /// let mut bytes = *b"hello";
398 /// let s = StrArray::<5>::mut_from_utf8(&mut bytes).unwrap();
399 /// assert_eq!(s, "hello");
400 /// ```
401 pub fn mut_from_utf8(val: &mut [u8; N]) -> Result<&mut Self, Utf8Error> {
402 match core::str::from_utf8(val) {
403 // SAFETY:
404 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`
405 // - `val` is valid UTF-8
406 Ok(_) => Ok(unsafe { &mut *(val as *mut [u8; N]).cast() }),
407 Err(e) => Err(e),
408 }
409 }
410 }
411
412 const_mut_fn! {
413 /// Converts UTF-8 bytes in `val` to `&mut StrArray<N>` without a UTF-8 check.
414 ///
415 /// # Examples
416 ///
417 /// ```
418 /// # use str_array::StrArray;
419 /// let mut bytes = *b"HELLO";
420 ///
421 /// // SAFETY: "HELLO" is valid UTF-8
422 /// let s: &mut StrArray<5> = unsafe { StrArray::mut_from_utf8_unchecked(&mut bytes) };
423 /// s.make_ascii_lowercase();
424 /// assert_eq!(s, "hello");
425 /// ```
426 ///
427 /// # Safety
428 ///
429 /// `val` must contain valid UTF-8 or behavior is undefined.
430 pub unsafe fn mut_from_utf8_unchecked(val: &mut [u8; N]) -> &mut Self {
431 // SAFETY:
432 // - `StrArray<N>` is `repr(transparent)` over `[u8; N]`.
433 // - The caller guarantees `val` is valid UTF-8.
434 unsafe { &mut *(val as *mut [u8; N]).cast() }
435 }
436 }
437
438 /// Returns a `StrArray<N>` filled with zeroes.
439 ///
440 /// # Examples
441 ///
442 /// ```
443 /// # use str_array::StrArray;
444 /// let s = StrArray::<4>::zeroed();
445 /// assert_eq!(s, "\0\0\0\0");
446 /// ```
447 pub const fn zeroed() -> Self {
448 Self([0; N])
449 }
450
451 /// Borrows this `StrArray<N>` as a `&str`.
452 ///
453 /// This is performed automatically on deref.
454 ///
455 /// # Examples
456 ///
457 /// ```
458 /// # use str_array::str_array;
459 /// let s = str_array!("hello");
460 /// assert_eq!(s.as_str().find('l'), Some(2));
461 /// assert_eq!(s.find('y'), None); // using deref
462 /// ```
463 pub const fn as_str(&self) -> &str {
464 // SAFETY: `self.0` is guaranteed to be UTF-8 bytes.
465 unsafe { core::str::from_utf8_unchecked(&self.0) }
466 }
467
468 const_mut_fn! {
469 /// Converts `&mut self` to a `&mut str`.
470 ///
471 /// This is performed automatically on deref.
472 ///
473 /// # Examples
474 ///
475 /// ```
476 /// # use str_array::str_array;
477 /// let mut s = str_array!("hello");
478 /// s.as_mut_str().make_ascii_uppercase();
479 /// assert_eq!(s, "HELLO");
480 /// ```
481 pub fn as_mut_str(&mut self) -> &mut str {
482 // SAFETY: `self.0` is guaranteed to be UTF-8 bytes.
483 unsafe { core::str::from_utf8_unchecked_mut(&mut self.0) }
484 }
485 }
486
487 /// Borrows `self` as a byte array of the same size.
488 ///
489 /// Unlike [`str::as_bytes`], this returns an array reference.
490 ///
491 /// # Example
492 ///
493 /// ```
494 /// # use str_array::{str_array, StrArray};
495 /// let x = str_array!("Hello");
496 /// let &[a, b @ .., c] = x.as_bytes();
497 /// assert_eq!(a, b'H');
498 /// assert_eq!(b, *b"ell");
499 /// assert_eq!(c, b'o');
500 /// ```
501 pub const fn as_bytes(&self) -> &[u8; N] {
502 &self.0
503 }
504
505 const_mut_fn! {
506 /// Borrows `self` as a mutable byte array of the same size.
507 ///
508 /// Unlike [`str::as_bytes_mut`], this returns an array reference.
509 ///
510 ///
511 /// # Examples
512 ///
513 /// ```
514 /// # use str_array::str_array;
515 /// let mut s = str_array!("hello");
516 /// unsafe {
517 /// let bytes = s.as_mut_bytes();
518 /// bytes[0] = b'H';
519 /// }
520 /// assert_eq!(s, "Hello");
521 /// ```
522 ///
523 /// # Safety
524 ///
525 /// This has the same non-local requirement as [`str::as_bytes_mut`]:
526 ///
527 /// The caller must ensure that the content of the array is valid UTF-8
528 /// before the borrow ends and the underlying `StrArray` is used.
529 pub unsafe fn as_mut_bytes(&mut self) -> &mut [u8; N] {
530 &mut self.0
531 }
532 }
533
534 /// Converts a `StrArray<N>` into a byte array.
535 ///
536 /// # Examples
537 ///
538 /// ```
539 /// # use str_array::{str_array, StrArray};
540 /// let x = str_array!("Fizzy");
541 ///
542 /// let [a, b @ .., c] = x.into_bytes();
543 /// assert_eq!(a, b'F');
544 /// assert_eq!(b, *b"izz");
545 /// assert_eq!(c, b'y');
546 /// ```
547 pub const fn into_bytes(self) -> [u8; N] {
548 self.0
549 }
550}
551
552impl StrArray<0> {
553 /// Returns an empty `StrArray`.
554 ///
555 /// # Examples
556 ///
557 /// ```
558 /// # use str_array::StrArray;
559 /// let s = StrArray::<0>::empty();
560 /// assert!(s.is_empty());
561 /// ```
562 pub const fn empty() -> Self {
563 Self([])
564 }
565}
566
567impl<const N: usize> Deref for StrArray<N> {
568 type Target = str;
569
570 fn deref(&self) -> &Self::Target {
571 self.as_str()
572 }
573}
574
575impl<const N: usize> DerefMut for StrArray<N> {
576 fn deref_mut(&mut self) -> &mut Self::Target {
577 self.as_mut_str()
578 }
579}
580
581/// Builds [`StrArray`] from constant `&str`.
582///
583/// # Examples
584///
585/// Pass a `const` expression of `&str` to build a `StrArray` with the same length.
586///
587/// ```
588/// # use str_array::str_array;
589/// let x = str_array!("Buzz");
590/// assert_eq!(x.len(), 4);
591///
592/// const NAME: &str = "Sally";
593/// let y = str_array!(NAME);
594/// assert_eq!(y, "Sally");
595/// ```
596///
597/// Define `static` or `const` items by eliding the type.
598/// The length of the `StrArray` uses the length of the assigned string.
599/// Note that the assignment expression currently is evaluated twice,
600/// but this should have no effect due to it being in `const`.
601///
602/// ```
603/// # use core::ptr::addr_of;
604/// # use str_array::{StrArray, str_array};
605/// str_array! {
606/// static STATIC = "static";
607/// static mut STATIC_MUT = "static_mut";
608/// const CONST = "constant";
609/// }
610/// assert_eq!(STATIC, "static");
611/// assert_eq!(unsafe { &*addr_of!(STATIC_MUT) }, "static_mut");
612/// assert_eq!(CONST, "constant");
613/// ```
614#[macro_export]
615macro_rules! str_array {
616 // This rule could be moved to another utility macro, but the inability to
617 // `macro_export` solely in the `mod __internal` leads me to leave this here.
618 // If only I could `doc(hidden)` utility segments of a macro_rules!
619 (@impl item
620 ($([$attr:meta])*)
621 ($($item_kind:tt)*)
622 $name:ident = $val:expr; $($rest:tt)*
623 ) => {
624 // Is there a way to avoid the double-evaluation of `$val` for item declarations
625 // without adding a dependency or proc-macro (i.e. use `paste`)?
626 $(#[$attr])* $($item_kind)* $name: $crate::StrArray<{$val.len()}> = match $crate::StrArray::new($val) {
627 Ok(a) => a,
628 Err(e) => e.const_panic(),
629 };
630 $crate::str_array!($($rest)*)
631 };
632 ($(#[$attr:meta])* static mut $($rest:tt)*) => {
633 $crate::str_array!(@impl item ($([$attr])*) (static mut) $($rest)*);
634 };
635 ($(#[$attr:meta])* static $($rest:tt)*) => {
636 $crate::str_array!(@impl item ($([$attr])*) (static) $($rest)*);
637 };
638 ($(#[$attr:meta])* const $($rest:tt)*) => {
639 $crate::str_array!(@impl item ($([$attr])*) (const) $($rest)*);
640 };
641 ($val:expr) => {{
642 const VAL: &str = $val;
643 const ARRAY: $crate::StrArray<{ VAL.len() }> = match $crate::StrArray::new(VAL) {
644 Ok(a) => a,
645 Err(e) => e.const_panic(),
646 };
647 ARRAY
648 }};
649 () => {};
650}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655
656 use ::alloc::format;
657
658 trait TypeEq {
659 type This;
660 }
661
662 impl<T> TypeEq for T {
663 type This = Self;
664 }
665
666 fn assert_type_eq_all<T, U>(_: U)
667 where
668 T: TypeEq<This = U>,
669 U:,
670 {
671 }
672
673 #[test]
674 fn test_deref_mut() {
675 let mut a = str_array!("aoeu");
676 a.make_ascii_uppercase();
677 assert_eq!(a, "AOEU");
678 }
679
680 #[test]
681 #[cfg(has_const_mut)]
682 fn test_const_mut() {
683 const X: StrArray<3> = {
684 let mut x = str_array!("foo");
685 x.as_mut_str().make_ascii_uppercase();
686 x
687 };
688 assert_eq!(X, "FOO");
689 }
690
691 #[test]
692 #[deny(non_upper_case_globals)]
693 fn test_macro_declared_items() {
694 str_array! {
695 /// With multi-line doc string
696 static STATIC = "hello";
697
698 #[allow(non_upper_case_globals)]
699 static mut StaticMut = "aoeu";
700 const CONST = "constant";
701 }
702 assert_eq!(STATIC, "hello");
703 assert_type_eq_all::<StrArray<5>, _>(STATIC);
704 assert_eq!(unsafe { StaticMut }, "aoeu");
705 assert_type_eq_all::<StrArray<4>, _>(unsafe { StaticMut });
706
707 let x = unsafe { &mut *core::ptr::addr_of_mut!(StaticMut) };
708 x.make_ascii_uppercase();
709 assert_eq!(x, "AOEU");
710
711 assert_eq!(CONST, "constant");
712 assert_type_eq_all::<StrArray<8>, _>(CONST);
713 }
714
715 #[test]
716 fn test_slice() {
717 let a = str_array!("a");
718 let _: &str = &a[..];
719 }
720
721 #[test]
722 fn test_zeroed() {
723 let s = StrArray::<4>::zeroed();
724 assert_eq!(s.as_bytes(), &[0, 0, 0, 0]);
725 }
726
727 #[test]
728 fn test_empty() {
729 let s = StrArray::empty();
730 assert!(s.is_empty());
731 }
732
733 #[test]
734 fn test_display() {
735 let s = str_array!("hello");
736 assert_eq!(format!("{}", s), "hello");
737 }
738
739 #[test]
740 fn test_debug() {
741 let s = str_array!("hello");
742 assert_eq!(format!("{:?}", s), "StrArray<5>(\"hello\")");
743 }
744}