Skip to main content

docsrs_cfg/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use proc_macro::*;
4use proc_macro::Spacing::*;
5use proc_macro::Delimiter::*;
6use proc_macro::TokenTree as T;
7
8
9#[doc = include_str!("../README.md")]
10#[proc_macro_attribute]
11pub fn docsrs_cfg(attr: TokenStream, item: TokenStream) -> TokenStream {
12    macro_impl("docsrs", attr, item)
13}
14
15/// Like [`macro@docsrs_cfg`], but use `doc` instead of `docsrs`
16///
17/// ```
18/// use docsrs_cfg::doc_cfg;
19/// #[doc_cfg(feature = "std")]
20/// fn foo() {}
21/// ```
22///
23/// Expand to:
24///
25/// ```
26/// #[cfg(any(doc, feature = "std"))]
27/// #[cfg_attr(doc, doc(cfg(feature = "std")))]
28/// fn foo() {}
29/// ```
30#[proc_macro_attribute]
31pub fn doc_cfg(attr: TokenStream, item: TokenStream) -> TokenStream {
32    macro_impl("doc", attr, item)
33}
34
35fn macro_impl(kind: &str, attr: TokenStream, item: TokenStream) -> TokenStream {
36    let span = Span::call_site();
37    [
38        TokenStream::from(T::Punct(Punct::new('#', Joint))),
39        TokenStream::from(T::Group(Group::new(Bracket, [
40            T::Ident(Ident::new("cfg", span)),
41            T::Group(Group::new(Parenthesis, [
42                T::Ident(Ident::new("any", span)),
43                T::Group(Group::new(Parenthesis, [
44                    T::Ident(Ident::new(kind, span)).into(),
45                    T::Punct(Punct::new(',', Alone)).into(),
46                    attr.clone(),
47                ].into_iter().collect())),
48            ].into_iter().collect())),
49        ].into_iter().collect()))),
50        TokenStream::from(T::Punct(Punct::new('#', Spacing::Joint))),
51        TokenStream::from(T::Group(Group::new(Bracket, [
52            T::Ident(Ident::new("cfg_attr", span)),
53            T::Group(Group::new(Parenthesis, [
54                T::Ident(Ident::new(kind, span)),
55                T::Punct(Punct::new(',', Alone)),
56                T::Ident(Ident::new("doc", span)),
57                T::Group(Group::new(Parenthesis, [
58                    T::Ident(Ident::new("cfg", span)),
59                    T::Group(Group::new(Parenthesis, attr)),
60                ].into_iter().collect())),
61            ].into_iter().collect())),
62        ].into_iter().collect()))),
63        item,
64    ].into_iter().collect()
65}
66
67/// ```
68/// #[docsrs_cfg::__test_docsrs(any(feature = "std", feature = "alloc"))]
69/// fn foo() {}
70/// ```
71#[doc(hidden)]
72#[proc_macro_attribute]
73pub fn __test_docsrs(attr: TokenStream, item: TokenStream) -> TokenStream {
74    let output = docsrs_cfg(attr, item.clone()).to_string()
75        .parse::<TokenStream>().unwrap().to_string();
76    let expect = r#"
77#[cfg(any(docsrs, any(feature = "std", feature = "alloc")))]
78#[cfg_attr(docsrs, doc(cfg(any(feature = "std", feature = "alloc"))))]
79fn foo() {}
80    "#.parse::<TokenStream>().unwrap().to_string();
81
82    if output.trim() == expect.trim() {
83        item
84    } else {
85        let span = Span::call_site();
86        [
87            T::Ident(Ident::new("compile_error", span)),
88            T::Punct(Punct::new('!', Joint)),
89            T::Group(Group::new(Brace, T::Literal(
90                Literal::string(&format!(
91                    "assert failed, expected:\n{}\ncurrent:\n{}",
92                    expect.trim(),
93                    output.trim(),
94                ))
95            ).into())),
96        ].into_iter().collect()
97    }
98}
99
100/// ```
101/// #[docsrs_cfg::__test_doc(any(feature = "std", feature = "alloc"))]
102/// fn foo() {}
103/// ```
104#[doc(hidden)]
105#[proc_macro_attribute]
106pub fn __test_doc(attr: TokenStream, item: TokenStream) -> TokenStream {
107    let output = doc_cfg(attr, item.clone()).to_string()
108        .parse::<TokenStream>().unwrap().to_string();
109    let expect = r#"
110#[cfg(any(doc, any(feature = "std", feature = "alloc")))]
111#[cfg_attr(doc, doc(cfg(any(feature = "std", feature = "alloc"))))]
112fn foo() {}
113    "#.parse::<TokenStream>().unwrap().to_string();
114
115    if output.trim() == expect.trim() {
116        item
117    } else {
118        let span = Span::call_site();
119        [
120            T::Ident(Ident::new("compile_error", span)),
121            T::Punct(Punct::new('!', Joint)),
122            T::Group(Group::new(Brace, T::Literal(
123                Literal::string(&format!(
124                    "assert failed, expected:\n{}\ncurrent:\n{}",
125                    expect.trim(),
126                    output.trim(),
127                ))
128            ).into())),
129        ].into_iter().collect()
130    }
131}