diff options
Diffstat (limited to 'third_party/rust/cstr/src/lib.rs')
-rw-r--r-- | third_party/rust/cstr/src/lib.rs | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/third_party/rust/cstr/src/lib.rs b/third_party/rust/cstr/src/lib.rs new file mode 100644 index 0000000000..c9e69d924c --- /dev/null +++ b/third_party/rust/cstr/src/lib.rs @@ -0,0 +1,56 @@ +//! A macro for getting `&'static CStr` from literal or identifier. +//! +//! This macro checks whether the given literal is valid for `CStr` +//! at compile time, and returns a static reference of `CStr`. +//! +//! This macro can be used to to initialize constants on Rust 1.64 and above. +//! +//! ## Example +//! +//! ``` +//! use cstr::cstr; +//! use std::ffi::CStr; +//! +//! let test = cstr!(b"hello\xff"); +//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\xff\0").unwrap()); +//! let test = cstr!("hello"); +//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); +//! let test = cstr!(hello); +//! assert_eq!(test, CStr::from_bytes_with_nul(b"hello\0").unwrap()); +//! ``` + +// While this isn't necessary when using Cargo >= 1.42, omitting it actually requires path-less +// `--extern proc_macro` to be passed to `rustc` when building this crate. Some tools may not do +// this correctly. So it's added as a precaution. +extern crate proc_macro; + +use crate::parse::parse_input; +use proc_macro::TokenStream as RawTokenStream; +use proc_macro2::{Literal, Span, TokenStream}; +use quote::{quote, quote_spanned}; +use std::ffi::CString; + +mod parse; + +struct Error(Span, &'static str); + +#[proc_macro] +pub fn cstr(input: RawTokenStream) -> RawTokenStream { + let tokens = match build_byte_str(input.into()) { + Ok(s) => quote!(unsafe { ::core::ffi::CStr::from_bytes_with_nul_unchecked(#s) }), + Err(Error(span, msg)) => quote_spanned!(span => compile_error!(#msg)), + }; + tokens.into() +} + +fn build_byte_str(input: TokenStream) -> Result<Literal, Error> { + let (bytes, span) = parse_input(input)?; + match CString::new(bytes) { + Ok(s) => { + let mut lit = Literal::byte_string(s.as_bytes_with_nul()); + lit.set_span(span); + Ok(lit) + } + Err(_) => Err(Error(span, "nul byte found in the literal")), + } +} |