Я пытаюсь написать макрос для своего рода библиотеки форматирования. Идея состоит в том, что вы можете использовать сочетание строковых литералов, обычных функций и (здесь я застрял) выведенных/ограниченных функций. Я подозреваю, что я мог бы заставить это работать с макросами proc, но это сложнее, и я бы хотел, чтобы код макроса был проще для чтения, если это возможно.
Вот урезанная версия кода, который я пишу.
enum TemplateEntry<T> {
Text(String),
Typed(T, String),
}
enum ColorType {
Red(String),
Blue(String),
}
impl ColorType {
pub fn red(s: &str) -> Self {
Self::Red(s.to_string())
pub fn blue(s: &str) -> Self {
Self::Blue(s.to_string())
}
}
impl Into<TemplateEntry<ColorType>> for ColorType {
fn into(&self) -> TemplateEntry<Self> {
match self {
Self::Blue(text) => TemplateEntry::Typed(self.clone(), text.clone()),
Self::Red(text) => TemplateEntry::Typed(self.clone(), text.clone()),
}
}
}
macro_rules! markup_helper {
($temp_vec:ident, $type_id:tt, @ $x:expr) => {{
$temp_vec.push($type_id::$x);
}};
( $temp_vec:ident, $type_id:tt, $x:expr) => {{
$temp_vec.push($x.into());
}};
}
#[macro_export]
macro_rules! markup {
( $type_id:tt, $( $($opt:literal)? $rest:expr ),* ) => {
{
let mut temp_vec: Vec<TemplateEntry<$type_id>> = Vec::new();
$(
markup_helper!(temp_vec, $type_id, $($opt)? $rest);
)*
temp_vec
}
};
}
fn example_call() {
let _example = markup!(ColorType, "regular text", @blue("other text"), "more regular text", regular_func_returning_text());
// What I want it to expand to
let _example = {
let mut temp_vec: Vec<TemplateEntry<ColorType>> = Vec::new();
temp_vec.push("regular text".into());
temp_vec.push(ColorType::blue("other text"));
temp_vec.push("more regular text".into());
temp_vec.push(regular_func_returning_text().into());
temp_vec
};
}
Поиск @ не работает должным образом, поскольку компилятор не может определить, являются ли строковые литералы необязательными литералами или выражением. Кроме того, похоже, он не пересылает @ помощнику, если я пытаюсь проанализировать его с помощью рекурсивно определенного макроса.
Есть ли способ использовать use, чтобы попытаться найти связанные функции базового типа, возможно, без индикатора @?
Есть ли что-то очевидное, чего мне не хватает для того, чтобы заставить работать @ или любой другой удобный символ индикатора, который позволил бы работать такому макросу?
Основная проблема в том, что вы не сопоставляете @, blue или круглые скобки в @blue("other text"), также неясно, что вы хотите, чтобы это значило.
Я обновил вопрос, указав, во что я хочу расширить макрос. Я ожидаю, что $rest:expr будет соответствовать части blue("other text"), которая кажется при написании менее сложных макросов для экспериментов.
Пожалуйста, отредактируйте свой вопрос, включив в него несколько примеров входных данных и то, чего вы ожидаете от них.
@blue не является буквальным. Это символ @, за которым следует ident, поэтому его следует анализировать как $(@ $opt:ident)?То, что вы указали как желаемое расширение макроса, не скомпилируется по ряду причин. Я бы попросил вас сначала это исправить, чтобы мы могли быть уверены, что понимаем, чего вы хотите. Однако, возможно, что-то вроде этого — это то, что вам нужно?
Пример @eggyal делает то, что я хочу. Спасибо, что собрали все вместе и проработали мой плохо объясненный вопрос. Синтаксис макроса кажется слишком сложным, поэтому я попробую сократить его и добавить в него ответ. Если вы захотите разместить это сами, я буду рад это принять.
Одним из упрощений может быть изменение правила @ так, чтобы оно только расширяло этот символ: см. пример. В противном случае очень рад, что вы упростите дальше по своему усмотрению и опубликуете в качестве ответа.

Благодаря @eggyal, вот окончательная версия, к которой я пришел, и она почти полностью соответствует тому, что они предоставили, только что переработанному с учетом моих личных предпочтений.
macro_rules! markup_internal {
// Expand leading `@` into `$type_id::`
(($temp_vec:ident:$type_id:ident): [@ $($rest:tt)*]) => {
markup_internal!(($temp_vec:$type_id): [$type_id::$($rest)*])
};
// Handle all other entries
(($temp_vec:ident:$type_id:ident): [$expr:expr$(, $($rest:tt)*)?]) => {
// Actually append the entries
$temp_vec.push($expr.into());
// Recursively expand
$(markup_internal!(($temp_vec:$type_id): [$($rest)*]))?
};
// Supports trailing commas
(($temp_vec:ident:$type_id:ident): []) => {}
}
#[macro_export]
macro_rules! markup {
// set up the array and push each list item into it by recursively invoking this macro
($type_id:ident: [$($rest:tt)*]) => {{
let mut temp_vec = Vec::<TemplateEntry<$type_id>>::new();
markup_internal!((temp_vec:$type_id): [$($rest)*]);
temp_vec
}};
}
Мне не ясно, чего вы хотите/ожидаете, что вызов макроса расширится?