Я использую include_str!()
, чтобы импортировать строку из файла и передать ее в proc_marco, но это не работает. Я получаю ошибку expected string literal
. Вот мой код:
macro_rules! ptcl_layer {
() => {
include_str!("tcp_to_msg.layer")
};
}
pub static TEMPLETE: &'static str = ptcl_layer!();
pub static TEMPLETE_HEADER: &'static str = get_protocol_layer_header!(ptcl_layer!());
pub static TEMPLETE_END: &'static str = get_protocol_layer_end!(ptcl_layer!());
#[proc_macro]
pub fn get_protocol_layer_header(_s: TokenStream) -> TokenStream {
let total_str = parse_macro_input!(_s as LitStr).value();
let header = total_str.lines().next().unwrap();
let header = header.replace("${.LEN}\n", "");
let output = quote::quote! {
#header
};
TokenStream::from(output)
}
#[proc_macro]
pub fn get_protocol_layer_end(_s: TokenStream) -> TokenStream {
let input = parse_macro_input!(_s as LitStr);
let input = input.value();
let last_line = input.lines().last().unwrap_or("");
let output = quote::quote! {
#last_line
};
TokenStream::from(output)
}
Полное сообщение об ошибке:
error: expected string literal
--> src/controller/mod.rs:16:71
|
16 | pub static TEMPLETE_HEADER: &'static str = get_protocol_layer_header!(ptcl_layer!());
| ^^^^^^^^^^
И *.layer вроде:
<? lines: ${.LEN}
${.CONTENT}
<? end
Я также попробовал другую версию кода:
#[proc_macro]
pub fn get_protocol_layer_header(_s: TokenStream) -> TokenStream {
let total_str = _s.to_string();
let header = total_str.lines().next().unwrap();
let header = header.replace("${.LEN}\n", "");
header.parse().unwrap()
}
#[proc_macro]
pub fn get_protocol_layer_end(_s: TokenStream) -> TokenStream {
let total_str = _s.to_string();
let last_line = total_str.lines().last().unwrap_or("");
last_line.parse().unwrap()
}
Но он вернул исходный текст без какой-либо обработки.
Как заметил eqqyal, по умолчанию макросы оцениваются снаружи, но когда какое-то выражение приведет к литералу, как в вашем случае, вы можете инвертировать это, используя нестабильный TokenStream::expand_expr:
вверху your_macros/src/lib.rs
:
#![feature(proc_macro_expand)]
в определениях, которые содержат LitStr
, вставьте следующее:
#[proc_macro]
pub fn get_protocol_layer_header(input: TokenStream) -> TokenStream {
let input = input.expand_expr().unwrap();
let total_str = parse_macro_input!(input as LitStr).value();
// …
Это то, что должны принимать самые внешние макросы, которые вы хотите использовать, поэтому, если вы не можете изменить определение get_protocol_layer_header
и ему подобных или не можете использовать каждую ночь, вам придется обойти это и вставить соответствующие строковые литералы с помощью build.rs
srcipt. .
Макросы расширяются снаружи внутрь, поэтому ваш макрос
get_protocol_layer_header!
вызывается с входными токенамиptcl_layer!()
, а затем, если его выходные данные содержат макрос, такой какptcl_layer!()
, то они будут расширены следующими.