Skip to content

Replace cstr16! with a declarative macro #1145

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions uefi-macros/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down
44 changes: 0 additions & 44 deletions uefi-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::<Result<Vec<u16>, _>>()
{
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(),
}
}
2 changes: 1 addition & 1 deletion uefi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down
17 changes: 8 additions & 9 deletions uefi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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;
Expand All @@ -133,25 +138,19 @@ 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() {
let _empty1 = cstr8!();
let _empty2 = cstr8!("");
let _regular = cstr8!("foobar");
}

#[test]
fn cstr16_macro_literal() {
let _empty1 = cstr16!();
let _empty2 = cstr16!("");
let _regular = cstr16!("foobar");
}
}
36 changes: 36 additions & 0 deletions uefi/src/macros.rs
Original file line number Diff line number Diff line change
@@ -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) }
}};
}
Loading