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}