wstr_literal/lib.rs
1#![doc = include_str!("../README.md")]
2use proc_macro::TokenStream;
3use wstr_literal_impl::{wstr_impl, wstr_literal_impl};
4
5/// Converts a string literal to a UTF-16 null-terminated array at compile time.
6///
7/// # Usage
8///
9/// - `wstr!("hello")` → `[u16; 6]` array containing UTF-16 code units + null terminator
10/// - `wstr!(10, "hello")` → `[u16; 10]` array, zero-padded to specified length
11///
12/// # Examples
13///
14/// ```rust
15/// use wstr_literal::wstr;
16///
17/// // Basic usage
18/// let greeting = wstr!("Hello");
19/// assert_eq!(&greeting, &['H' as u16, 'e' as u16, 'l' as u16, 'l' as u16, 'o' as u16, 0]);
20///
21/// // Fixed length with padding
22/// let padded = wstr!(10, "Hello");
23/// assert_eq!(padded.len(), 10);
24/// assert_eq!(&padded[..6], &['H' as u16, 'e' as u16, 'l' as u16, 'l' as u16, 'o' as u16, 0]); // utf16 code units + null terminator
25/// assert_eq!(&padded[6..], &[0u16, 0u16, 0u16, 0u16]); // zero padding
26/// ```
27///
28/// The following will fail to compile because the specified length is too small:
29///
30/// ```compile_fail
31/// use wstr_literal::wstr;
32/// let too_small = wstr!(3, "hello");
33/// // Error: array size must be at least length of input string plus null terminator
34/// ```
35///
36/// # Requirements
37///
38/// - Input must be a string literal (not a variable or expression)
39/// - Optional length must be an integer literal
40/// - If fixed size is specified, it must be at least string length + 1(for null terminator)
41///
42#[proc_macro]
43pub fn wstr(input: TokenStream) -> TokenStream {
44 wstr_impl(input.into())
45 .unwrap_or_else(syn::Error::into_compile_error)
46 .into()
47}
48
49/// Attribute macro that transforms string literal values in `const` or `static` declarations into null-terminated UTF-16 arrays.
50///
51/// Apply this attribute to `const` or `static` declarations to automatically convert
52/// string literals into null-terminated UTF-16 arrays at compile time.
53///
54/// <div class="warning">
55/// This macro only works with const and static declarations. For let bindings,
56/// use wstr! macro instead.
57/// </div>
58///
59/// # Usage
60///
61/// - `#[wstr_literal] const NAME: [u16; _] = "text";` → length inferred automatically
62/// - `#[wstr_literal] static NAME: [u16; 10] = "text";` → fixed length with zero padding
63///
64/// # Examples
65///
66/// ```rust
67/// use wstr_literal::wstr_literal;
68///
69/// // Length inferred from string content
70/// #[wstr_literal]
71/// const GREETING: [u16; _] = "Hello";
72/// // Expands to: const GREETING: [u16; 6] = [72, 101, 108, 108, 111, 0];
73///
74/// // Fixed length with padding
75/// #[wstr_literal]
76/// static PADDED: [u16; 10] = "Hi";
77/// // Expands to: static PADDED: [u16; 10] = [72, 105, 0, 0, 0, 0, 0, 0, 0, 0];
78///
79/// // Works with visibility, mutability, and other attributes
80/// #[wstr_literal]
81/// #[allow(non_upper_case_globals)]
82/// pub static mut Global: [u16; _] = "data";
83/// ```
84///
85/// The following will fail to compile because the array size (1) is too small
86/// for the string "hello" which needs 6 elements (5 UTF-16 code units + 1 null terminator):
87///
88/// ```compile_fail
89/// use wstr_literal::wstr_literal;
90/// #[wstr_literal]
91/// static HELLO: [u16; 1] = "hello";
92/// // Error: array size must be at least length of input string plus null terminator
93/// ```
94///
95/// # Requirements
96///
97/// - Target must be a `const` or `static` declaration
98/// - Type must be `[u16; SIZE]` where `SIZE` is either `_` or a integer literal
99/// - The initial value must be a string literal (not a variable or expression)
100/// - If fixed size is specified, it must be at least string length + 1(for null terminator)
101///
102#[proc_macro_attribute]
103pub fn wstr_literal(attr: TokenStream, item: TokenStream) -> TokenStream {
104 if !attr.is_empty() {
105 return syn::Error::new_spanned(
106 proc_macro2::TokenStream::from(attr),
107 "The attribute does not take any arguments",
108 )
109 .into_compile_error()
110 .into();
111 }
112
113 wstr_literal_impl(item.into())
114 .unwrap_or_else(syn::Error::into_compile_error)
115 .into()
116}