sp_phragmen_compact/
lib.rs1use proc_macro::TokenStream;
21use proc_macro2::{TokenStream as TokenStream2, Span, Ident};
22use proc_macro_crate::crate_name;
23use quote::quote;
24use syn::{GenericArgument, Type, parse::{Parse, ParseStream, Result}};
25
26mod assignment;
27mod staked;
28
29const PREFIX: &'static str = "votes";
31
32#[proc_macro]
80pub fn generate_compact_solution_type(item: TokenStream) -> TokenStream {
81 let CompactSolutionDef {
82 vis,
83 ident,
84 count,
85 } = syn::parse_macro_input!(item as CompactSolutionDef);
86
87 let voter_type = GenericArgument::Type(Type::Verbatim(quote!(V)));
88 let target_type = GenericArgument::Type(Type::Verbatim(quote!(T)));
89 let weight_type = GenericArgument::Type(Type::Verbatim(quote!(W)));
90
91 let imports = imports().unwrap_or_else(|e| e.to_compile_error());
92
93 let compact_def = struct_def(
94 vis,
95 ident.clone(),
96 count,
97 voter_type.clone(),
98 target_type.clone(),
99 weight_type,
100 ).unwrap_or_else(|e| e.to_compile_error());
101
102 let assignment_impls = assignment::assignment(
103 ident.clone(),
104 voter_type.clone(),
105 target_type.clone(),
106 count,
107 );
108
109 let staked_impls = staked::staked(
110 ident,
111 voter_type,
112 target_type,
113 count,
114 );
115
116 quote!(
117 #imports
118 #compact_def
119 #assignment_impls
120 #staked_impls
121 ).into()
122}
123
124fn struct_def(
125 vis: syn::Visibility,
126 ident: syn::Ident,
127 count: usize,
128 voter_type: GenericArgument,
129 target_type: GenericArgument,
130 weight_type: GenericArgument,
131) -> Result<TokenStream2> {
132 if count <= 2 {
133 Err(syn::Error::new(
134 Span::call_site(),
135 "cannot build compact solution struct with capacity less than 2."
136 ))?
137 }
138
139 let singles = {
140 let name = field_name_for(1);
141 quote!(#name: Vec<(#voter_type, #target_type)>,)
142 };
143
144 let doubles = {
145 let name = field_name_for(2);
146 quote!(#name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>,)
147 };
148
149 let rest = (3..=count).map(|c| {
150 let field_name = field_name_for(c);
151 let array_len = c - 1;
152 quote!(
153 #field_name: Vec<(
154 #voter_type,
155 [(#target_type, #weight_type); #array_len],
156 #target_type
157 )>,
158 )
159 }).collect::<TokenStream2>();
160
161
162 let len_impl = (1..=count).map(|c| {
163 let field_name = field_name_for(c);
164 quote!(
165 all_len = all_len.saturating_add(self.#field_name.len());
166 )
167 }).collect::<TokenStream2>();
168
169 let edge_count_impl = (1..count).map(|c| {
170 let field_name = field_name_for(c);
171 quote!(
172 all_edges = all_edges.saturating_add(
173 self.#field_name.len().saturating_mul(#c as usize)
174 );
175 )
176 }).collect::<TokenStream2>();
177
178 Ok(quote! (
179 #[derive(
181 Default,
182 PartialEq,
183 Eq,
184 Clone,
185 Debug,
186 _phragmen::codec::Encode,
187 _phragmen::codec::Decode,
188 )]
189 #vis struct #ident<#voter_type, #target_type, #weight_type> {
190 #singles
192 #doubles
193 #rest
194 }
195
196 impl<#voter_type, #target_type, #weight_type> _phragmen::VotingLimit
197 for #ident<#voter_type, #target_type, #weight_type>
198 {
199 const LIMIT: usize = #count;
200 }
201
202 impl<#voter_type, #target_type, #weight_type> #ident<#voter_type, #target_type, #weight_type> {
203 pub fn len(&self) -> usize {
206 let mut all_len = 0usize;
207 #len_impl
208 all_len
209 }
210
211 pub fn edge_count(&self) -> usize {
213 let mut all_edges = 0usize;
214 #edge_count_impl
215 all_edges
216 }
217
218 pub fn average_edge_count(&self) -> usize {
220 self.edge_count().checked_div(self.len()).unwrap_or(0)
221 }
222 }
223 ))
224}
225
226fn imports() -> Result<TokenStream2> {
227 let sp_phragmen_imports = match crate_name("sp-phragmen") {
228 Ok(sp_phragmen) => {
229 let ident = syn::Ident::new(&sp_phragmen, Span::call_site());
230 quote!( extern crate #ident as _phragmen; )
231 }
232 Err(e) => return Err(syn::Error::new(Span::call_site(), &e)),
233 };
234
235 Ok(quote!(
236 #sp_phragmen_imports
237 ))
238}
239
240struct CompactSolutionDef {
241 vis: syn::Visibility,
242 ident: syn::Ident,
243 count: usize,
244}
245
246impl Parse for CompactSolutionDef {
247 fn parse(input: ParseStream) -> syn::Result<Self> {
248 let vis: syn::Visibility = input.parse()?;
249 let ident: syn::Ident = input.parse()?;
250 let _ = <syn::Token![,]>::parse(input)?;
251 let count_literal: syn::LitInt = input.parse()?;
252 let count = count_literal.base10_parse::<usize>()?;
253 Ok(Self { vis, ident, count } )
254 }
255}
256
257fn field_name_for(n: usize) -> Ident {
258 Ident::new(&format!("{}{}", PREFIX, n), Span::call_site())
259}