frame_support_procedural/pallet/expand/
documentation.rs1use crate::pallet::Def;
19use proc_macro2::TokenStream;
20use quote::ToTokens;
21use syn::{spanned::Spanned, Attribute, Lit, LitStr};
22
23const DOC: &'static str = "doc";
24const PALLET_DOC: &'static str = "pallet_doc";
25
26fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result<DocMetaValue> {
31 let lit: syn::LitStr = attr.parse_args().map_err(|_| {
32 let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(\"PATH\")`";
33 syn::Error::new(attr.span(), msg)
34 })?;
35
36 Ok(DocMetaValue::Path(lit))
37}
38
39fn parse_doc_value(attr: &Attribute) -> syn::Result<Option<DocMetaValue>> {
45 if !attr.path().is_ident(DOC) {
46 return Ok(None)
47 }
48
49 let meta = attr.meta.require_name_value()?;
50
51 match &meta.value {
52 syn::Expr::Lit(lit) => Ok(Some(DocMetaValue::Lit(lit.lit.clone()))),
53 syn::Expr::Macro(mac) if mac.mac.path.is_ident("include_str") =>
54 Ok(Some(DocMetaValue::Path(mac.mac.parse_body()?))),
55 _ =>
56 Err(syn::Error::new(attr.span(), "Expected `= \"docs\"` or `= include_str!(\"PATH\")`")),
57 }
58}
59
60#[derive(Debug)]
62enum DocMetaValue {
63 Lit(Lit),
67 Path(LitStr),
73}
74
75impl ToTokens for DocMetaValue {
76 fn to_tokens(&self, tokens: &mut TokenStream) {
77 match self {
78 DocMetaValue::Lit(lit) => lit.to_tokens(tokens),
79 DocMetaValue::Path(path_lit) => {
80 let decl = quote::quote!(include_str!(#path_lit));
81 tokens.extend(decl)
82 },
83 }
84 }
85}
86
87pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream {
113 let frame_support = &def.frame_support;
114 let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
115 let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
116 let pallet_ident = &def.pallet_struct.pallet;
117 let where_clauses = &def.config.where_clause;
118
119 let mut pallet_docs = Vec::with_capacity(def.item.attrs.len());
124 let mut index = 0;
125 while index < def.item.attrs.len() {
126 let attr = &def.item.attrs[index];
127 if attr.path().get_ident().map_or(false, |i| *i == PALLET_DOC) {
128 pallet_docs.push(def.item.attrs.remove(index));
129 continue
132 }
133
134 index += 1;
135 }
136
137 let docs = match def
139 .item
140 .attrs
141 .iter()
142 .filter_map(|v| parse_doc_value(v).transpose())
143 .collect::<syn::Result<Vec<_>>>()
144 {
145 Ok(r) => r,
146 Err(err) => return err.into_compile_error(),
147 };
148
149 let pallet_docs = match pallet_docs
151 .into_iter()
152 .map(|attr| parse_pallet_doc_value(&attr))
153 .collect::<syn::Result<Vec<_>>>()
154 {
155 Ok(docs) => docs,
156 Err(err) => return err.into_compile_error(),
157 };
158
159 let docs = docs.iter().chain(pallet_docs.iter());
160
161 quote::quote!(
162 impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{
163
164 #[doc(hidden)]
165 pub fn pallet_documentation_metadata()
166 -> #frame_support::__private::Vec<&'static str>
167 {
168 #frame_support::__private::vec![ #( #docs ),* ]
169 }
170 }
171 )
172}