1#![doc = include_str!("../README.md")]
2#![deny(unsafe_op_in_unsafe_fn)]
3#![no_std]
4
5#[doc(hidden)]
6pub mod private {
7 use core::ffi::CStr;
8
9 pub extern crate const_format;
10
11 pub const fn new(s: &'static str) -> &'static CStr {
12 let mut bytes = s.as_bytes();
13 loop {
14 match bytes {
15 [0, _, ..] => panic!("C strings can't contain null bytes"),
16 [] => panic!("C strings must be null-terminated"),
17 [0] => break,
18 [_, remaining @ ..] => bytes = remaining,
19 }
20 }
21
22 unsafe { CStr::from_bytes_with_nul_unchecked(s.as_bytes()) }
23 }
24}
25
26#[macro_export]
33macro_rules! cstr {
34 ($s:expr) => {{
35 const __CSTR_STR: &str = $crate::private::const_format::concatcp!($s, "\0");
36 const __CSTR: &::core::ffi::CStr = $crate::private::new(__CSTR_STR);
37 __CSTR
38 }};
39}
40
41#[cfg(test)]
42mod tests {
43 #[test]
44 fn valid() {
45 let t = trybuild::TestCases::new();
46 t.pass("tests/valid/*.rs");
47 }
48
49 #[test]
50 fn invalid() {
51 let t = trybuild::TestCases::new();
52 t.compile_fail("tests/invalid/*.rs");
53 }
54}