stack_cstr/macros.rs
1/// A macro to create a C-compatible string (`&CStr`) with stack allocation fallback.
2///
3/// The `cstr!` macro tries to construct a [`CStrStack`] with one of the given
4/// buffer sizes. If none of the stack sizes is large enough, it falls back to
5/// allocating a [`CString`] on the heap.
6///
7/// This makes it ergonomic to create FFI-safe strings with minimal overhead.
8/// Common short strings will use stack buffers, while longer strings
9/// automatically use heap allocation.
10///
11/// # Syntax
12///
13/// ```ignore
14/// cstr!([sizes...], "format string", args...)
15/// cstr!("format string", args...)   // uses default sizes [32, 128]
16/// ```
17///
18/// - `sizes...`: A list of candidate stack buffer sizes.  
19///   The macro tries each size in order until one succeeds.  
20///   If all fail, a heap allocation is used.  
21/// - `"format string", args...`: A format string and its arguments,
22///   just like in `format!`.
23///
24/// # Returns
25///
26/// A `Box<dyn CStrLike>`, which can be used to obtain:
27/// - a raw pointer (`*const c_char`) via [`CStrLike::as_ptr`]
28/// - a reference to the [`CStr`] via [`CStrLike::as_cstr`]
29///
30/// # Examples
31///
32/// ```
33/// use std::ffi::CStr;
34/// use stack_cstr::{cstr, CStrLike};
35///
36/// // Use default sizes [32, 128]
37/// let s = cstr!("Hello {}", 42);
38/// assert_eq!(s.as_cstr().to_str().unwrap(), "Hello 42");
39///
40/// // Explicit stack sizes
41/// let s = cstr!([16, 64], "Pi = {:.2}", 3.14159);
42/// assert_eq!(s.as_cstr().to_str().unwrap(), "Pi = 3.14");
43///
44/// unsafe {
45///     // Pass to FFI as *const c_char
46///     assert_eq!(CStr::from_ptr(s.as_ptr()).to_str().unwrap(), "Pi = 3.14");
47/// }
48/// ```
49///
50/// # Notes
51///
52/// - If the formatted string fits in one of the provided stack buffers,
53///   no heap allocation is performed.  
54/// - If the string is too long for all stack buffers, it is allocated on the heap.
55/// - The returned type is `Box<dyn CStrLike>`, which is heap-allocated for
56///   type erasure even when the string is stack-based. This indirection is
57///   usually negligible.
58///
59/// # See also
60///
61/// - [`CStrStack`] for stack-only storage
62/// - [`CStrHeap`] for explicit heap allocation
63#[macro_export]
64macro_rules! cstr {
65    ([$($size:expr),*], $($args:tt)*) => {{
66        let args = format_args!($($args)*);
67
68        let result: Box<dyn $crate::CStrLike> = if false { unreachable!() }
69        $(
70            else if let Ok(s) = $crate::CStrStack::<$size>::new(args) {
71                Box::new(s)
72            }
73        )*
74        else {
75            Box::new($crate::CStrHeap::new(std::ffi::CString::new(format!($($args)*)).unwrap()))
76        };
77
78        result
79    }};
80    ($($args:tt)*) => {
81        cstr!([32, 128], $($args)*)
82    };
83}