as_derive_utils/gen_params_in.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
//! Contains the `GenParamsIn` type,for printing generic parameters.
use syn::{
token::{Colon, Comma, Const, Star},
GenericParam, Generics,
};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use crate::utils::NoTokens;
/// Used to print (with ToTokens) the generc parameter differently depending on where
/// it is being used/declared.
///
/// One can also add stuff to be printed after lifetime/type parameters.
#[derive(Debug, Copy, Clone)]
pub struct GenParamsIn<'a, AL = NoTokens> {
pub generics: &'a Generics,
/// Where the generic parameters are being printed.
pub in_what: InWhat,
/// Whether type parameters are all `?Sized`
pub unsized_types: bool,
/// Whether type bounds will be printed in type parameter declarations.
pub with_bounds: bool,
skip_lifetimes: bool,
/// What will be printed after lifetime parameters
after_lifetimes: Option<AL>,
/// What will be printed after type parameters
after_types: Option<AL>,
skip_consts: bool,
skip_unbounded: bool,
}
/// Where the generic parameters are being printed.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum InWhat {
/// For the header of an impl block,as in the contents of `impl< ... >`.
ImplHeader,
/// For trait/struct/enum/union
ItemDecl,
/// For when using a generic as an argument for a trait/struct/enum/union.
ItemUse,
/// For defining the fields of a Dummy struct that is never instantiated,
/// only using its associated items.
DummyStruct,
}
impl<'a> GenParamsIn<'a> {
#[allow(dead_code)]
pub fn new(generics: &'a Generics, in_what: InWhat) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: None,
after_types: None,
skip_consts: false,
skip_unbounded: false,
}
}
}
impl<'a, AL> GenParamsIn<'a, AL> {
/// Constructs a GenParamsIn that prints `after lifetimes` after lifetime parameters.
pub fn with_after_lifetimes(
generics: &'a Generics,
in_what: InWhat,
after_lifetimes: AL,
) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: Some(after_lifetimes),
after_types: None,
skip_consts: false,
skip_unbounded: false,
}
}
/// Constructs a GenParamsIn that prints `after types` after type parameters.
pub fn with_after_types(generics: &'a Generics, in_what: InWhat, after_types: AL) -> Self {
Self {
generics,
in_what,
unsized_types: false,
with_bounds: true,
skip_lifetimes: false,
after_lifetimes: None,
after_types: Some(after_types),
skip_consts: false,
skip_unbounded: false,
}
}
/// Removes the bounds on type parameters on type parameter declarations.
pub fn set_no_bounds(&mut self) {
self.with_bounds = false;
}
/// Makes all type parameters `?Sized`.
#[allow(dead_code)]
pub fn set_unsized_types(&mut self) {
self.unsized_types = true;
}
pub fn skip_lifetimes(&mut self) {
self.skip_lifetimes = true;
}
pub fn skip_consts(&mut self) {
self.skip_consts = true;
}
pub fn skip_unbounded(&mut self) {
self.skip_unbounded = true;
self.skip_consts();
self.skip_lifetimes();
}
pub fn skips_unbounded(&self) -> bool {
self.skip_unbounded
}
/// Queries whether bounds are printed.
pub fn outputs_bounds(&self) -> bool {
self.with_bounds && matches!(self.in_what, InWhat::ImplHeader | InWhat::ItemDecl)
}
/// Queries whether all types have a `?Sized` bound.
pub fn are_types_unsized(&self) -> bool {
self.unsized_types && matches!(self.in_what, InWhat::ItemDecl | InWhat::ImplHeader)
}
}
impl<'a, AL> ToTokens for GenParamsIn<'a, AL>
where
AL: ToTokens,
{
fn to_tokens(&self, ts: &mut TokenStream) {
let with_bounds = self.outputs_bounds();
let with_default = self.in_what == InWhat::ItemDecl;
let in_dummy_struct = self.in_what == InWhat::DummyStruct;
let unsized_types = self.are_types_unsized();
let mut iter = self.generics.params.iter().peekable();
let comma = Comma::default();
let brace = syn::token::Brace::default();
if self.skip_lifetimes {
while let Some(GenericParam::Lifetime { .. }) = iter.peek() {
iter.next();
}
} else {
while let Some(GenericParam::Lifetime(gen)) = iter.peek() {
iter.next();
if in_dummy_struct {
syn::token::And::default().to_tokens(ts);
gen.lifetime.to_tokens(ts);
syn::token::Paren::default().surround(ts, |_| ());
} else {
gen.lifetime.to_tokens(ts);
if with_bounds {
gen.colon_token.to_tokens(ts);
gen.bounds.to_tokens(ts);
}
}
comma.to_tokens(ts);
}
}
self.after_lifetimes.to_tokens(ts);
while let Some(GenericParam::Type(gen)) = iter.peek() {
iter.next();
if gen.bounds.is_empty() && self.skip_unbounded {
continue;
}
if in_dummy_struct {
Star::default().to_tokens(ts);
Const::default().to_tokens(ts);
}
gen.ident.to_tokens(ts);
if (with_bounds && gen.colon_token.is_some()) || unsized_types {
Colon::default().to_tokens(ts);
if unsized_types {
quote!(?Sized+).to_tokens(ts);
}
if with_bounds {
gen.bounds.to_tokens(ts);
}
}
if with_default {
gen.eq_token.to_tokens(ts);
gen.default.to_tokens(ts);
}
comma.to_tokens(ts);
}
self.after_types.to_tokens(ts);
if !in_dummy_struct && !self.skip_consts {
while let Some(GenericParam::Const(gen)) = iter.peek() {
iter.next();
if self.in_what != InWhat::ItemUse {
gen.const_token.to_tokens(ts);
}
if self.in_what == InWhat::ItemUse {
// Have to surround the const parameter when it's used,
// because otherwise it's interpreted as a type parameter.
// Remove this branch once outputting the identifier
// for the const parameter just works.
brace.surround(ts, |ts| {
gen.ident.to_tokens(ts);
});
} else {
gen.ident.to_tokens(ts);
}
if self.in_what != InWhat::ItemUse {
Colon::default().to_tokens(ts);
gen.ty.to_tokens(ts);
}
if with_default {
gen.eq_token.to_tokens(ts);
gen.default.to_tokens(ts);
}
comma.to_tokens(ts);
}
}
}
}