diff --git a/uefi-macros/CHANGELOG.md b/uefi-macros/CHANGELOG.md index a86e687e4..9ddd357fa 100644 --- a/uefi-macros/CHANGELOG.md +++ b/uefi-macros/CHANGELOG.md @@ -1,5 +1,9 @@ # uefi-macros - [Unreleased] +## Removed +- Removed the `cstr16` macro. Use the `cstr16` declarative macro exported by the + `uefi` crate instead. + # uefi-macros - 0.13.0 (2023-11-12) ## Changed diff --git a/uefi-macros/src/lib.rs b/uefi-macros/src/lib.rs index 971bf2c9f..635494304 100644 --- a/uefi-macros/src/lib.rs +++ b/uefi-macros/src/lib.rs @@ -288,47 +288,3 @@ pub fn cstr8(input: proc_macro::TokenStream) -> proc_macro::TokenStream { .into(), } } - -/// Builds a `CStr16` literal at compile time from a string literal. -/// -/// This will throw a compile error if an invalid character is in the passed string. -/// -/// # Example -/// ```rust -/// # use uefi_macros::cstr16; -/// // Empty string -/// assert_eq!(cstr16!().to_u16_slice_with_nul(), [0]); -/// assert_eq!(cstr16!("").to_u16_slice_with_nul(), [0]); -/// // Non-empty string -/// assert_eq!(cstr16!("test €").to_u16_slice_with_nul(), [116, 101, 115, 116, 32, 8364, 0]); -/// ``` -#[proc_macro] -pub fn cstr16(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Accept empty input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - let input: LitStr = parse_macro_input!(input); - let input = input.value(); - // Accept "" input. - if input.is_empty() { - return quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[0]) }).into(); - } - - // Accept any non-empty string input. - match input - .chars() - .map(|c| u16::try_from(c as u32)) - .collect::, _>>() - { - Ok(c) => { - quote!(unsafe { ::uefi::CStr16::from_u16_with_nul_unchecked(&[ #(#c),* , 0 ]) }).into() - } - Err(_) => syn::Error::new_spanned( - input, - "There are UTF-8 characters that can't be transformed to UCS-2 character", - ) - .into_compile_error() - .into(), - } -} diff --git a/uefi/Cargo.toml b/uefi/Cargo.toml index ce50ed6a2..fdb2e64fb 100644 --- a/uefi/Cargo.toml +++ b/uefi/Cargo.toml @@ -30,7 +30,7 @@ log.workspace = true ptr_meta.workspace = true uguid.workspace = true cfg-if = "1.0.0" -ucs2 = "0.3.2" +ucs2 = "0.3.3" uefi-macros = "0.13.0" uefi-raw = "0.5.2" qemu-exit = { version = "3.0.2", optional = true } diff --git a/uefi/src/lib.rs b/uefi/src/lib.rs index 0179af23b..651e1b3d7 100644 --- a/uefi/src/lib.rs +++ b/uefi/src/lib.rs @@ -100,6 +100,11 @@ extern crate alloc; // see https://github.com/rust-lang/rust/issues/54647 extern crate self as uefi; +/// Re-export ucs2_cstr so that it can be used in the implementation of the +/// cstr16 macro. It is hidden since it's not intended to be used directly. +#[doc(hidden)] +pub use ucs2::ucs2_cstr; + #[macro_use] extern crate uefi_raw; @@ -108,7 +113,7 @@ pub mod data_types; #[cfg(feature = "alloc")] pub use data_types::CString16; pub use data_types::{CStr16, CStr8, Char16, Char8, Event, Guid, Handle, Identify}; -pub use uefi_macros::{cstr16, cstr8, entry}; +pub use uefi_macros::{cstr8, entry}; pub use uguid::guid; mod result; @@ -133,13 +138,14 @@ pub(crate) mod polyfill; pub mod helpers; +mod macros; mod util; #[cfg(test)] // Crates that create procedural macros can't unit test the macros they export. // Therefore, we do some tests here. mod macro_tests { - use crate::{cstr16, cstr8}; + use crate::cstr8; #[test] fn cstr8_macro_literal() { @@ -147,11 +153,4 @@ mod macro_tests { let _empty2 = cstr8!(""); let _regular = cstr8!("foobar"); } - - #[test] - fn cstr16_macro_literal() { - let _empty1 = cstr16!(); - let _empty2 = cstr16!(""); - let _regular = cstr16!("foobar"); - } } diff --git a/uefi/src/macros.rs b/uefi/src/macros.rs new file mode 100644 index 000000000..4e9897157 --- /dev/null +++ b/uefi/src/macros.rs @@ -0,0 +1,36 @@ +/// Encode a string literal as a [`&CStr16`]. +/// +/// The encoding is done at compile time, so the result can be used in a +/// `const` item. +/// +/// An empty string containing just a null character can be created with either +/// `cstr16!()` or `cstr16!("")`. +/// +/// # Example +/// +/// ``` +/// use uefi::{CStr16, cstr16}; +/// +/// const S: &CStr16 = cstr16!("abc"); +/// assert_eq!(S.to_u16_slice_with_nul(), [97, 98, 99, 0]); +/// +/// const EMPTY: &CStr16 = cstr16!(); +/// assert_eq!(EMPTY.to_u16_slice_with_nul(), [0]); +/// assert_eq!(cstr16!(""), EMPTY); +/// ``` +/// +/// [`&CStr16`]: crate::CStr16 +#[macro_export] +macro_rules! cstr16 { + () => {{ + const S: &[u16] = &[0]; + // SAFETY: `S` is a trivially correct UCS-2 C string. + unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } + }}; + ($s:literal) => {{ + const S: &[u16] = &$crate::ucs2_cstr!($s); + // SAFETY: the ucs2_cstr macro always produces a valid UCS-2 string with + // a trailing null character. + unsafe { $crate::CStr16::from_u16_with_nul_unchecked(S) } + }}; +}