Создание документации в макросах

У меня есть несколько макросов для уменьшения шаблона при определении определенных кортежей-структур формы:

macro_rules! new_type (($name:ident, $bytes:expr) => (
    pub struct $name(pub [u8; $bytes]);
    // some common operations on $name
));

Однако я хотел бы также документировать эти новые структуры. Лучше всего было бы, если бы я мог написать свою документацию прямо перед вызовом макроса.

/// A certain type
new_type!(CertainType, 42);

Однако, когда это произойдет, Rust не будет генерировать документацию для CertainType.

Другой (не такой гибкий) вариант должен был бы сделать что-то вроде:

macro_rules! new_type (($name:ident, $bytes:expr) => (
    /// Some more generic documentation for $name 
    pub struct $name(pub [u8; $bytes]);
    // some common operations on $name
));

Однако при этом макрос Rust не расширяет токен $name в комментарии к документации. Единственная альтернатива - написать очень общую документацию в макросе, но это приведет к тому, что моя библиотека будет намного хуже документирована, чем это могло бы быть.

Каковы ваши рекомендации по работе с этим? Лучшим решением для меня было бы написать конкретную документацию для каждого вызова макроса, но если бы это было невозможно, я был бы признателен за советы о том, как развернуть маркеры в комментариях к документации.

Ответы

Ответ 1

Возможно захватить комментарии документа в вызовах макроса. Это не широко известно, но документация Rust фактически представлена как особый вид атрибута для элемента. Например:

/// Some documentation comment
pub fn function() {}

// is equivalent to

#[doc="Some documentation comment"]
pub fn function() {}

И можно захватывать атрибуты в макросах. Уже есть несколько макросов, которые используют эту способность, наиболее часто используемыми являются bitflags! :

macro_rules! bitflags {
    (
        $(#[$outer:meta])*
        pub struct $BitFlags:ident: $T:ty {
            $(
                $(#[$inner:ident $($args:tt)*])*
                const $Flag:ident = $value:expr;
            )+
        }
    ) => { /* ... */ };
    // ...
}

Обратите внимание на части шаблона $(#[$outer:meta])* и $(#[$inner:meta])*. Они захватывают все атрибуты, помещенные перед соответствующим элементом в шаблоне. Если вы напишите там комментарий к документу, он будет преобразован в атрибут документа и будет передан rustdoc, как обычно.

Ниже приведен пример из ящика quick_error который также использует этот подход:

quick_error! {
    #[derive(Debug)]
    pub enum SomeError {
        /// IO Error
        Io(err: io::Error) {}
        /// Arbitrary system error
        Sys(errno: nix::Errno) {}
    }
}

Это работает - вот пример структуры, сгенерированной quick_error, и вот ее определение.