Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit c1283b9

Browse files
author
command-bot
committed
2 parents b77f768 + 3a94009 commit c1283b9

File tree

14 files changed

+412
-8
lines changed

14 files changed

+412
-8
lines changed

client/consensus/slots/src/lib.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
190190
proposer: Self::Proposer,
191191
claim: &Self::Claim,
192192
slot_info: SlotInfo<B>,
193-
proposing_remaining: Delay,
193+
end_proposing_at: Instant,
194194
) -> Option<
195195
Proposal<
196196
B,
@@ -202,9 +202,11 @@ pub trait SimpleSlotWorker<B: BlockT> {
202202
let telemetry = self.telemetry();
203203
let log_target = self.logging_target();
204204

205-
let inherent_data = Self::create_inherent_data(&slot_info, &log_target).await?;
205+
let inherent_data =
206+
Self::create_inherent_data(&slot_info, &log_target, end_proposing_at).await?;
206207

207-
let proposing_remaining_duration = self.proposing_remaining_duration(&slot_info);
208+
let proposing_remaining_duration =
209+
end_proposing_at.saturating_duration_since(Instant::now());
208210
let logs = self.pre_digest_data(slot, claim);
209211

210212
// deadline our production to 98% of the total time left for proposing. As we deadline
@@ -219,7 +221,12 @@ pub trait SimpleSlotWorker<B: BlockT> {
219221
)
220222
.map_err(|e| sp_consensus::Error::ClientImport(e.to_string()));
221223

222-
let proposal = match futures::future::select(proposing, proposing_remaining).await {
224+
let proposal = match futures::future::select(
225+
proposing,
226+
Delay::new(proposing_remaining_duration),
227+
)
228+
.await
229+
{
223230
Either::Left((Ok(p), _)) => p,
224231
Either::Left((Err(err), _)) => {
225232
warn!(target: log_target, "Proposing failed: {}", err);
@@ -255,8 +262,9 @@ pub trait SimpleSlotWorker<B: BlockT> {
255262
async fn create_inherent_data(
256263
slot_info: &SlotInfo<B>,
257264
logging_target: &str,
265+
end_proposing_at: Instant,
258266
) -> Option<sp_inherents::InherentData> {
259-
let remaining_duration = slot_info.ends_at.saturating_duration_since(Instant::now());
267+
let remaining_duration = end_proposing_at.saturating_duration_since(Instant::now());
260268
let delay = Delay::new(remaining_duration);
261269
let cid = slot_info.create_inherent_data.create_inherent_data();
262270
let inherent_data = match futures::future::select(delay, cid).await {
@@ -300,15 +308,15 @@ pub trait SimpleSlotWorker<B: BlockT> {
300308

301309
let proposing_remaining_duration = self.proposing_remaining_duration(&slot_info);
302310

303-
let proposing_remaining = if proposing_remaining_duration == Duration::default() {
311+
let end_proposing_at = if proposing_remaining_duration == Duration::default() {
304312
debug!(
305313
target: logging_target,
306314
"Skipping proposal slot {} since there's no time left to propose", slot,
307315
);
308316

309317
return None
310318
} else {
311-
Delay::new(proposing_remaining_duration)
319+
Instant::now() + proposing_remaining_duration
312320
};
313321

314322
let aux_data = match self.aux_data(&slot_info.chain_head, slot) {
@@ -379,7 +387,7 @@ pub trait SimpleSlotWorker<B: BlockT> {
379387
},
380388
};
381389

382-
let proposal = self.propose(proposer, &claim, slot_info, proposing_remaining).await?;
390+
let proposal = self.propose(proposer, &claim, slot_info, end_proposing_at).await?;
383391

384392
let (block, storage_proof) = (proposal.block, proposal.proof);
385393
let (header, body) = block.deconstruct();

frame/support/procedural/src/lib.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -481,6 +481,57 @@ pub fn construct_runtime(input: TokenStream) -> TokenStream {
481481
/// </pre></div>
482482
///
483483
/// See `frame_support::pallet` docs for more info.
484+
///
485+
/// ## Runtime Metadata Documentation
486+
///
487+
/// The documentation added to this pallet is included in the runtime metadata.
488+
///
489+
/// The documentation can be defined in the following ways:
490+
///
491+
/// ```ignore
492+
/// #[pallet::pallet]
493+
/// /// Documentation for pallet 1
494+
/// #[doc = "Documentation for pallet 2"]
495+
/// #[doc = include_str!("../README.md")]
496+
/// #[pallet_doc("../doc1.md")]
497+
/// #[pallet_doc("../doc2.md")]
498+
/// pub struct Pallet<T>(_);
499+
/// ```
500+
///
501+
/// The runtime metadata for this pallet contains the following
502+
/// - " Documentation for pallet 1" (captured from `///`)
503+
/// - "Documentation for pallet 2" (captured from `#[doc]`)
504+
/// - content of ../README.md (captured from `#[doc]` with `include_str!`)
505+
/// - content of "../doc1.md" (captured from `pallet_doc`)
506+
/// - content of "../doc2.md" (captured from `pallet_doc`)
507+
///
508+
/// ### `doc` attribute
509+
///
510+
/// The value of the `doc` attribute is included in the runtime metadata, as well as
511+
/// expanded on the pallet module. The previous example is expanded to:
512+
///
513+
/// ```ignore
514+
/// /// Documentation for pallet 1
515+
/// /// Documentation for pallet 2
516+
/// /// Content of README.md
517+
/// pub struct Pallet<T>(_);
518+
/// ```
519+
///
520+
/// If you want to specify the file from which the documentation is loaded, you can use the
521+
/// `include_str` macro. However, if you only want the documentation to be included in the
522+
/// runtime metadata, use the `pallet_doc` attribute.
523+
///
524+
/// ### `pallet_doc` attribute
525+
///
526+
/// Unlike the `doc` attribute, the documentation provided to the `pallet_doc` attribute is
527+
/// not inserted on the module.
528+
///
529+
/// The `pallet_doc` attribute can only be provided with one argument,
530+
/// which is the file path that holds the documentation to be added to the metadata.
531+
///
532+
/// This approach is beneficial when you use the `include_str` macro at the beginning of the file
533+
/// and want that documentation to extend to the runtime metadata, without reiterating the
534+
/// documentation on the module itself.
484535
#[proc_macro_attribute]
485536
pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream {
486537
pallet::pallet(attr, item)
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
// This file is part of Substrate.
2+
3+
// Copyright (C) Parity Technologies (UK) Ltd.
4+
// SPDX-License-Identifier: Apache-2.0
5+
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
18+
use crate::pallet::Def;
19+
use derive_syn_parse::Parse;
20+
use proc_macro2::TokenStream;
21+
use quote::ToTokens;
22+
use syn::{
23+
parse::{self, Parse, ParseStream},
24+
spanned::Spanned,
25+
Attribute, Lit,
26+
};
27+
28+
const DOC: &'static str = "doc";
29+
const PALLET_DOC: &'static str = "pallet_doc";
30+
31+
mod keywords {
32+
syn::custom_keyword!(include_str);
33+
}
34+
35+
/// Get the documentation file path from the `pallet_doc` attribute.
36+
///
37+
/// Supported format:
38+
/// `#[pallet_doc(PATH)]`: The path of the file from which the documentation is loaded
39+
fn parse_pallet_doc_value(attr: &Attribute) -> syn::Result<DocMetaValue> {
40+
let span = attr.span();
41+
42+
let meta = attr.parse_meta()?;
43+
let syn::Meta::List(metalist) = meta else {
44+
let msg = "The `pallet_doc` attribute must receive arguments as a list. Supported format: `pallet_doc(PATH)`";
45+
return Err(syn::Error::new(span, msg))
46+
};
47+
48+
let paths: Vec<_> = metalist
49+
.nested
50+
.into_iter()
51+
.map(|nested| {
52+
let syn::NestedMeta::Lit(lit) = nested else {
53+
let msg = "The `pallet_doc` received an unsupported argument. Supported format: `pallet_doc(PATH)`";
54+
return Err(syn::Error::new(span, msg))
55+
};
56+
57+
Ok(lit)
58+
})
59+
.collect::<syn::Result<_>>()?;
60+
61+
if paths.len() != 1 {
62+
let msg = "The `pallet_doc` attribute must receive only one argument. Supported format: `pallet_doc(PATH)`";
63+
return Err(syn::Error::new(span, msg))
64+
}
65+
66+
Ok(DocMetaValue::Path(paths[0].clone()))
67+
}
68+
69+
/// Get the value from the `doc` comment attribute:
70+
///
71+
/// Supported formats:
72+
/// - `#[doc = "A doc string"]`: Documentation as a string literal
73+
/// - `#[doc = include_str!(PATH)]`: Documentation obtained from a path
74+
fn parse_doc_value(attr: &Attribute) -> Option<DocMetaValue> {
75+
let Some(ident) = attr.path.get_ident() else {
76+
return None
77+
};
78+
if ident != DOC {
79+
return None
80+
}
81+
82+
let parser = |input: ParseStream| DocParser::parse(input);
83+
let result = parse::Parser::parse2(parser, attr.tokens.clone()).ok()?;
84+
85+
if let Some(lit) = result.lit {
86+
Some(DocMetaValue::Lit(lit))
87+
} else if let Some(include_doc) = result.include_doc {
88+
Some(DocMetaValue::Path(include_doc.lit))
89+
} else {
90+
None
91+
}
92+
}
93+
94+
/// Parse the include_str attribute.
95+
#[derive(Debug, Parse)]
96+
struct IncludeDocParser {
97+
_include_str: keywords::include_str,
98+
_eq_token: syn::token::Bang,
99+
#[paren]
100+
_paren: syn::token::Paren,
101+
#[inside(_paren)]
102+
lit: Lit,
103+
}
104+
105+
/// Parse the doc literal.
106+
#[derive(Debug, Parse)]
107+
struct DocParser {
108+
_eq_token: syn::token::Eq,
109+
#[peek(Lit)]
110+
lit: Option<Lit>,
111+
#[parse_if(lit.is_none())]
112+
include_doc: Option<IncludeDocParser>,
113+
}
114+
115+
/// Supported documentation tokens.
116+
#[derive(Debug)]
117+
enum DocMetaValue {
118+
/// Documentation with string literals.
119+
///
120+
/// `#[doc = "Lit"]`
121+
Lit(Lit),
122+
/// Documentation with `include_str!` macro.
123+
///
124+
/// The string literal represents the file `PATH`.
125+
///
126+
/// `#[doc = include_str!(PATH)]`
127+
Path(Lit),
128+
}
129+
130+
impl ToTokens for DocMetaValue {
131+
fn to_tokens(&self, tokens: &mut TokenStream) {
132+
match self {
133+
DocMetaValue::Lit(lit) => lit.to_tokens(tokens),
134+
DocMetaValue::Path(path_lit) => {
135+
let decl = quote::quote!(include_str!(#path_lit));
136+
tokens.extend(decl)
137+
},
138+
}
139+
}
140+
}
141+
142+
/// Extract the documentation from the given pallet definition
143+
/// to include in the runtime metadata.
144+
///
145+
/// Implement a `pallet_documentation_metadata` function to fetch the
146+
/// documentation that is included in the metadata.
147+
///
148+
/// The documentation is placed at the top of the module similar to:
149+
///
150+
/// ```ignore
151+
/// #[pallet]
152+
/// /// Documentation for pallet
153+
/// #[doc = "Documentation for pallet"]
154+
/// #[doc = include_str!("../README.md")]
155+
/// #[pallet_doc("../documentation1.md")]
156+
/// #[pallet_doc("../documentation2.md")]
157+
/// pub mod pallet {}
158+
/// ```
159+
///
160+
/// # pallet_doc
161+
///
162+
/// The `pallet_doc` attribute can only be provided with one argument,
163+
/// which is the file path that holds the documentation to be added to the metadata.
164+
///
165+
/// Unlike the `doc` attribute, the documentation provided to the `proc_macro` attribute is
166+
/// not inserted at the beginning of the module.
167+
pub fn expand_documentation(def: &mut Def) -> proc_macro2::TokenStream {
168+
let frame_support = &def.frame_support;
169+
let type_impl_gen = &def.type_impl_generics(proc_macro2::Span::call_site());
170+
let type_use_gen = &def.type_use_generics(proc_macro2::Span::call_site());
171+
let pallet_ident = &def.pallet_struct.pallet;
172+
let where_clauses = &def.config.where_clause;
173+
174+
// TODO: Use [drain_filter](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.drain_filter) when it is stable.
175+
176+
// The `pallet_doc` attributes are excluded from the generation of the pallet,
177+
// but they are included in the runtime metadata.
178+
let mut pallet_docs = Vec::with_capacity(def.item.attrs.len());
179+
let mut index = 0;
180+
while index < def.item.attrs.len() {
181+
let attr = &def.item.attrs[index];
182+
if let Some(ident) = attr.path.get_ident() {
183+
if ident == PALLET_DOC {
184+
let elem = def.item.attrs.remove(index);
185+
pallet_docs.push(elem);
186+
// Do not increment the index, we have just removed the
187+
// element from the attributes.
188+
continue
189+
}
190+
}
191+
192+
index += 1;
193+
}
194+
195+
// Capture the `#[doc = include_str!("../README.md")]` and `#[doc = "Documentation"]`.
196+
let mut docs: Vec<_> = def.item.attrs.iter().filter_map(parse_doc_value).collect();
197+
198+
// Capture the `#[pallet_doc("../README.md")]`.
199+
let pallet_docs: Vec<_> = match pallet_docs
200+
.into_iter()
201+
.map(|attr| parse_pallet_doc_value(&attr))
202+
.collect::<syn::Result<_>>()
203+
{
204+
Ok(docs) => docs,
205+
Err(err) => return err.into_compile_error(),
206+
};
207+
208+
docs.extend(pallet_docs);
209+
210+
quote::quote!(
211+
impl<#type_impl_gen> #pallet_ident<#type_use_gen> #where_clauses{
212+
213+
#[doc(hidden)]
214+
pub fn pallet_documentation_metadata()
215+
-> #frame_support::sp_std::vec::Vec<&'static str>
216+
{
217+
#frame_support::sp_std::vec![ #( #docs ),* ]
218+
}
219+
}
220+
)
221+
}

frame/support/procedural/src/pallet/expand/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
mod call;
1919
mod config;
2020
mod constants;
21+
mod documentation;
2122
mod error;
2223
mod event;
2324
mod genesis_build;
@@ -52,6 +53,8 @@ pub fn merge_where_clauses(clauses: &[&Option<syn::WhereClause>]) -> Option<syn:
5253
/// * create some new types,
5354
/// * impl stuff on them.
5455
pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
56+
// Remove the `pallet_doc` attribute first.
57+
let metadata_docs = documentation::expand_documentation(&mut def);
5558
let constants = constants::expand_constants(&mut def);
5659
let pallet_struct = pallet_struct::expand_pallet_struct(&mut def);
5760
let config = config::expand_config(&mut def);
@@ -82,6 +85,7 @@ pub fn expand(mut def: Def) -> proc_macro2::TokenStream {
8285
}
8386

8487
let new_items = quote::quote!(
88+
#metadata_docs
8589
#constants
8690
#pallet_struct
8791
#config

0 commit comments

Comments
 (0)