diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c90b5c5f..6d6475e5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ - Added `FilePathMediaDevicePath`. - Added `DevicePath::as_acpi_device_path` and `DevicePath::as_file_path_media_device_path`. +- Included `cstr8` and `cstr16` macros from `uefi-macros` in the prelude. - Added `DevicePathInstance`, `DevicePathNode`, and `FfiDevicePath`. ### Changed @@ -32,6 +33,11 @@ ## uefi-macros - [Unreleased] +### Added + +- Added `cstr8` and `cstr16` macros for creating `CStr8`/`CStr16` string literals + at compile time. + ## uefi-services - [Unreleased] ## uefi - 0.15.2 diff --git a/src/data_types/owned_strs.rs b/src/data_types/owned_strs.rs index d41cc0de5..369e66a9d 100644 --- a/src/data_types/owned_strs.rs +++ b/src/data_types/owned_strs.rs @@ -150,16 +150,14 @@ mod tests { /// Test `CString16 == &CStr16` and `&CStr16 == CString16`. #[test] fn test_cstring16_cstr16_eq() { - let mut buf = [0; 4]; - assert_eq!( - CStr16::from_str_with_buf("abc", &mut buf).unwrap(), + crate::prelude::cstr16!("abc"), CString16::try_from("abc").unwrap() ); assert_eq!( CString16::try_from("abc").unwrap(), - CStr16::from_str_with_buf("abc", &mut buf).unwrap(), + crate::prelude::cstr16!("abc") ); } } diff --git a/src/data_types/strs.rs b/src/data_types/strs.rs index 748591f52..78ab70127 100644 --- a/src/data_types/strs.rs +++ b/src/data_types/strs.rs @@ -468,6 +468,15 @@ mod tests { ); } + #[test] + fn test_cstr16_macro() { + // Just a sanity check to make sure it's spitting out the right characters + assert_eq!( + crate::prelude::cstr16!("ABC").to_u16_slice_with_nul(), + [65, 66, 67, 0] + ) + } + #[test] fn test_unaligned_cstr16() { let mut buf = [0u16; 6]; diff --git a/src/prelude.rs b/src/prelude.rs index df13e435d..3a5dac089 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -9,5 +9,5 @@ pub use crate::table::boot::BootServices; pub use crate::table::runtime::RuntimeServices; pub use crate::table::{Boot, SystemTable}; -// Import the macro for creating the custom entry point. -pub use uefi_macros::entry; +// Import the macro for creating the custom entry point, as well as the cstr macros. +pub use uefi_macros::{cstr16, cstr8, entry}; diff --git a/uefi-macros/src/lib.rs b/uefi-macros/src/lib.rs index 50ec2eff8..dba437f4e 100644 --- a/uefi-macros/src/lib.rs +++ b/uefi-macros/src/lib.rs @@ -211,3 +211,57 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { }; result.into() } + +/// Builds a `CStr8` literal at compile time from a string literal. +/// +/// This will throw a compile error if an invalid character is in the passed string. +/// +/// # Example +/// ``` +/// # use uefi_macros::cstr8; +/// assert_eq!(cstr8!("test").to_bytes_with_nul(), [116, 101, 115, 116, 0]); +/// ``` +#[proc_macro] +pub fn cstr8(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let input: LitStr = parse_macro_input!(input); + let input = input.value(); + match input + .chars() + .map(u8::try_from) + .collect::, _>>() + { + Ok(c) => { + quote!(unsafe { ::uefi::CStr8::from_bytes_with_nul_unchecked(&[ #(#c),* , 0 ]) }).into() + } + Err(_) => syn::Error::new_spanned(input, "invalid character in string") + .into_compile_error() + .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 +/// ``` +/// # use uefi_macros::cstr16; +/// 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 { + let input: LitStr = parse_macro_input!(input); + let input = input.value(); + 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, "invalid character in string") + .into_compile_error() + .into(), + } +} diff --git a/uefi-test-runner/src/proto/media/known_disk.rs b/uefi-test-runner/src/proto/media/known_disk.rs index d56a1e410..1ac6f0f99 100644 --- a/uefi-test-runner/src/proto/media/known_disk.rs +++ b/uefi-test-runner/src/proto/media/known_disk.rs @@ -6,15 +6,14 @@ use uefi::proto::media::file::{ use uefi::proto::media::fs::SimpleFileSystem; use uefi::table::boot::{OpenProtocolAttributes, OpenProtocolParams}; use uefi::table::runtime::{Daylight, Time, TimeParams}; -use uefi::CString16; /// Test directory entry iteration. fn test_existing_dir(directory: &mut Directory) { info!("Testing existing directory"); - let input_dir_path = CString16::try_from("test_dir").unwrap(); + let input_dir_path = cstr16!("test_dir"); let mut dir = directory - .open(&input_dir_path, FileMode::Read, FileAttribute::empty()) + .open(input_dir_path, FileMode::Read, FileAttribute::empty()) .expect("failed to open directory") .into_directory() .expect("not a directory"); @@ -37,9 +36,9 @@ fn test_existing_dir(directory: &mut Directory) { /// warning. This is mostly just an excuse to verify that warnings are /// properly converted to errors. fn test_delete_warning(directory: &mut Directory) { - let input_file_path = CString16::try_from("test_dir\\test_input.txt").unwrap(); + let input_file_path = cstr16!("test_dir\\test_input.txt"); let file = directory - .open(&input_file_path, FileMode::Read, FileAttribute::empty()) + .open(input_file_path, FileMode::Read, FileAttribute::empty()) .expect("failed to open file") .into_regular_file() .expect("not a regular file"); @@ -55,13 +54,9 @@ fn test_existing_file(directory: &mut Directory) { info!("Testing existing file"); // Open an existing file. - let input_file_path = CString16::try_from("test_dir\\test_input.txt").unwrap(); + let input_file_path = cstr16!("test_dir\\test_input.txt"); let mut file = directory - .open( - &input_file_path, - FileMode::ReadWrite, - FileAttribute::empty(), - ) + .open(input_file_path, FileMode::ReadWrite, FileAttribute::empty()) .expect("failed to open file") .into_regular_file() .expect("not a regular file"); @@ -111,10 +106,7 @@ fn test_existing_file(directory: &mut Directory) { .unwrap() ); assert_eq!(info.attribute(), FileAttribute::empty()); - assert_eq!( - info.file_name(), - CString16::try_from("test_input.txt").unwrap() - ); + assert_eq!(info.file_name(), cstr16!("test_input.txt")); // Check that `get_boxed_info` returns the same info. let boxed_info = file.get_boxed_info::().unwrap(); @@ -125,7 +117,7 @@ fn test_existing_file(directory: &mut Directory) { // Verify the file is gone. assert!(directory - .open(&input_file_path, FileMode::Read, FileAttribute::empty()) + .open(input_file_path, FileMode::Read, FileAttribute::empty()) .is_err()); } @@ -136,7 +128,7 @@ fn test_create_file(directory: &mut Directory) { // Create a new file. let mut file = directory .open( - &CString16::try_from("new_test_file.txt").unwrap(), + cstr16!("new_test_file.txt"), FileMode::CreateReadWrite, FileAttribute::empty(), ) diff --git a/uefi-test-runner/src/runtime/vars.rs b/uefi-test-runner/src/runtime/vars.rs index f90d86b31..2bd8fc8ab 100644 --- a/uefi-test-runner/src/runtime/vars.rs +++ b/uefi-test-runner/src/runtime/vars.rs @@ -1,11 +1,10 @@ use log::info; use uefi::prelude::*; use uefi::table::runtime::{VariableAttributes, VariableVendor}; -use uefi::{CStr16, Guid}; +use uefi::Guid; fn test_variables(rt: &RuntimeServices) { - let mut buf = [0; 14]; - let name = CStr16::from_str_with_buf("UefiRsTestVar", &mut buf).unwrap(); + let name = cstr16!("UefiRsTestVar"); let test_value = b"TestValue"; let test_attrs = VariableAttributes::BOOTSERVICE_ACCESS | VariableAttributes::RUNTIME_ACCESS;