#![doc(html_root_url = "https://docs.rs/serde_closure_derive/0.3.2")]
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use std::{collections::HashSet, iter, iter::successors, mem::take, str};
use syn::{
parse2, parse_macro_input, visit_mut::{self, VisitMut}, Arm, AttributeArgs, Block, Error, Expr, ExprArray, ExprAssign, ExprAssignOp, ExprAsync, ExprAwait, ExprBinary, ExprBlock, ExprBox, ExprBreak, ExprCall, ExprCast, ExprClosure, ExprField, ExprForLoop, ExprGroup, ExprIf, ExprIndex, ExprLet, ExprLoop, ExprMacro, ExprMatch, ExprMethodCall, ExprParen, ExprPath, ExprRange, ExprReference, ExprRepeat, ExprReturn, ExprStruct, ExprTry, ExprTryBlock, ExprTuple, ExprType, ExprUnary, ExprUnsafe, ExprWhile, ExprYield, FieldValue, GenericParam, Ident, ImplItem, Item, ItemImpl, Lifetime, LifetimeDef, Local, Member, Pat, PatBox, PatIdent, PatReference, PatSlice, PatTuple, PatTupleStruct, PatType, Path, PathArguments, PathSegment, ReturnType, Stmt, TraitBound, Type, TypeInfer, TypeReference, TypeTuple, UnOp
};
#[proc_macro]
#[allow(non_snake_case)]
pub fn Fn(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
syn::parse::<ExprClosure>(input)
.and_then(|closure| impl_closure(closure, Kind::Fn))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro]
#[allow(non_snake_case)]
pub fn FnMut(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
syn::parse::<ExprClosure>(input)
.and_then(|closure| impl_closure(closure, Kind::FnMut))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro]
#[allow(non_snake_case)]
pub fn FnOnce(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
syn::parse::<ExprClosure>(input)
.and_then(|closure| impl_closure(closure, Kind::FnOnce))
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_attribute]
pub fn desugar(
attr: proc_macro::TokenStream, item: proc_macro::TokenStream,
) -> proc_macro::TokenStream {
let args: AttributeArgs = parse_macro_input!(attr);
assert_eq!(args.len(), 0);
let mut item = match syn::parse::<Item>(item) {
Err(err) => return err.to_compile_error().into(),
Ok(item) => item,
};
Desugar.visit_item_mut(&mut item);
item.into_token_stream().into()
}
struct Desugar;
impl Desugar {
fn desugar_path_arg(
&mut self, arg: &mut PathArguments, return_output: bool,
) -> (u64, Option<Type>) {
if let PathArguments::Parenthesized(args) = &arg {
let span = Span::call_site();
let mut lifetimes = 0;
let mut inputs = args.inputs.clone();
for input in &mut inputs {
match input {
Type::Reference(TypeReference { lifetime, .. }) if lifetime.is_none() => {
*lifetime = Some(Lifetime::new(
&format!(
"'__serde_closure_{}",
bijective_base(lifetimes, 26, alpha_lower)
),
span,
));
lifetimes += 1;
}
_ => (),
}
}
if !inputs.empty_or_trailing() {
inputs.push_punct(Default::default());
}
let output = match &args.output {
ReturnType::Type(_, type_) => (&**type_).clone(),
ReturnType::Default => Type::Tuple(TypeTuple {
paren_token: Default::default(),
elems: Default::default(),
}),
};
*arg = PathArguments::AngleBracketed(if !return_output {
syn::parse2(quote! { <(#inputs), Output = #output> }).unwrap()
} else {
syn::parse2(quote! { <(#inputs)> }).unwrap()
});
(lifetimes, if return_output { Some(output) } else { None })
} else {
(0, None)
}
}
}
impl VisitMut for Desugar {
fn visit_trait_bound_mut(&mut self, i: &mut TraitBound) {
let lifetimes = self
.desugar_path_arg(&mut i.path.segments.last_mut().unwrap().arguments, false)
.0;
if lifetimes > 0 {
let span = Span::call_site();
let empty = syn::parse2(quote! {for <>}).unwrap();
i.lifetimes = Some(i.lifetimes.clone().unwrap_or(empty));
i.lifetimes
.as_mut()
.unwrap()
.lifetimes
.extend((0..lifetimes).map(|i| {
LifetimeDef::new(Lifetime::new(
&format!("'__serde_closure_{}", bijective_base(i, 26, alpha_lower)),
span,
))
}));
}
visit_mut::visit_trait_bound_mut(self, i)
}
fn visit_item_impl_mut(&mut self, i: &mut ItemImpl) {
if let Some((_, path, _)) = &mut i.trait_ {
let (lifetimes, output) =
self.desugar_path_arg(&mut path.segments.last_mut().unwrap().arguments, true);
if lifetimes > 0 {
let span = Span::call_site();
i.generics.lt_token = Some(Default::default());
i.generics.gt_token = Some(Default::default());
i.generics.params.extend((0..lifetimes).map(|i| {
GenericParam::Lifetime(LifetimeDef::new(Lifetime::new(
&format!("'__serde_closure_{}", bijective_base(i, 26, alpha_lower)),
span,
)))
}));
}
if path.segments.last().unwrap().ident == "FnOnce" {
if let Some(output) = output {
i.items.push(ImplItem::Type(
syn::parse2(quote! { type Output = #output; }).unwrap(),
));
}
}
}
visit_mut::visit_item_impl_mut(self, i)
}
}
#[derive(Copy, Clone, PartialEq, Eq)]
enum Kind {
Fn,
FnMut,
FnOnce,
}
impl Kind {
fn name(self) -> &'static str {
match self {
Kind::Fn => "Fn",
Kind::FnMut => "FnMut",
Kind::FnOnce => "FnOnce",
}
}
}
#[allow(clippy::cognitive_complexity)]
fn impl_closure(mut closure: ExprClosure, kind: Kind) -> Result<TokenStream, Error> {
let span = Span::call_site();
let name_string = kind.name();
let name = Ident::new(name_string, span);
let env_name = Ident::new("__serde_closure_env", span);
let ret_name = Ident::new("__serde_closure_ret", span);
let env_types_name = Ident::new("__serde_closure_env_types", span);
let impls_name = Ident::new("__serde_closure_impls", span);
let source = closure.to_token_stream().to_string();
let capture = closure.capture.is_some();
closure.body = Box::new(match *closure.body {
Expr::Block(block) => Expr::Block(block),
Expr::Tuple(tuple) if tuple.elems.is_empty() => Expr::Tuple(tuple),
expr => Expr::Block(ExprBlock {
attrs: vec![],
label: None,
block: Block {
brace_token: Default::default(),
stmts: vec![Stmt::Expr(expr)],
},
}),
});
let mut closure = Expr::Closure(closure);
let mut env_variables = HashSet::new();
let mut not_env_variables = HashSet::new();
State::new(
&mut env_variables,
&mut not_env_variables,
kind != Kind::FnOnce,
kind != Kind::FnOnce && !capture,
&env_name,
)
.expr(&mut closure, false);
assert!(not_env_variables.is_empty());
let mut env_variables: Vec<Ident> = env_variables.into_iter().collect();
env_variables.sort();
let env_variables = &env_variables;
let env_variable_names = &env_variables
.iter()
.map(|ident| ident.to_string())
.collect::<Vec<_>>();
let closure = if let Expr::Closure(closure) = closure {
closure
} else {
unreachable!()
};
let attrs = closure.attrs;
let asyncness = closure.asyncness;
let capture = closure.capture;
let output = closure.output;
let body = closure.body;
let body = match *body {
Expr::Tuple(tuple) if tuple.elems.is_empty() => None,
expr => Some(expr),
};
let input_pats = closure.inputs.iter().map(|input| match input {
Pat::Type(pat_type) => (*pat_type.pat).clone(),
pat => (*pat).clone(),
});
let input_types = closure.inputs.iter().map(pat_to_type);
let type_params = &(0..env_variables.len())
.map(|i| {
Ident::new(
&format!(
"__SERDE_CLOSURE_{}",
bijective_base(i as u64, 26, alpha_upper)
),
span,
)
})
.collect::<Vec<_>>();
let type_params_infer = (0..env_variables.len()).map(|_| TypeInfer {
underscore_token: Default::default(),
});
let ret_ref: Expr = parse2(match kind {
Kind::Fn => quote! { &#ret_name },
Kind::FnMut => quote! { &mut #ret_name },
Kind::FnOnce => quote! { #ret_name },
})
.unwrap();
let env_deref: Expr = parse2(match kind {
Kind::Fn => quote! { *#env_name },
Kind::FnMut => quote! { *#env_name },
Kind::FnOnce => quote! { #env_name },
})
.unwrap();
let env_type: Type = parse2(match kind {
Kind::Fn => quote! { &#impls_name::#name<#(#type_params_infer,)*_> },
Kind::FnMut => quote! { &mut #impls_name::#name<#(#type_params_infer,)*_> },
Kind::FnOnce => quote! { #impls_name::#name<#(#type_params_infer,)*_> },
})
.unwrap();
let env_capture = parse2::<ExprTuple>(match (kind, capture.is_some()) {
(Kind::Fn, false) => quote! { ( #( & #env_variables ,)* ) },
(Kind::FnMut, false) => quote! { ( #( &mut #env_variables ,)* ) },
_ => quote! { ( #( #env_variables ,)* ) },
})
.unwrap()
.elems;
let env_deconstruct = if kind == Kind::FnOnce {
Some(
parse2::<Stmt>(
quote! { #[allow(unused_mut)] let #impls_name::#name{#(mut #env_variables ,)*..} = #env_name; },
)
.unwrap(),
)
} else {
None
};
let fn_impl = match kind {
Kind::Fn => quote! {
impl<#(#type_params,)* F, I, O> self::internal::FnOnce<I> for #name<#(#type_params,)* F>
where
F: ops::FnOnce(&#name<#(#type_params,)* ()>,I) -> O
{
type Output = O;
#[inline(always)]
fn call_once(self, args: I) -> Self::Output {
self.f()(&self.strip_f(), args)
}
}
impl<#(#type_params,)* F, I, O> self::internal::FnMut<I> for #name<#(#type_params,)* F>
where
F: ops::FnMut(&#name<#(#type_params,)* ()>,I) -> O
{
#[inline(always)]
fn call_mut(&mut self, args: I) -> Self::Output {
(&mut self.f())(self.strip_f_mut(), args)
}
}
impl<#(#type_params,)* F, I, O> self::internal::Fn<I> for #name<#(#type_params,)* F>
where
F: ops::Fn(&#name<#(#type_params,)* ()>,I) -> O
{
#[inline(always)]
fn call(&self, args: I) -> Self::Output {
(&self.f())(self.strip_f_ref(), args)
}
}
},
Kind::FnMut => quote! {
impl<#(#type_params,)* F, I, O> self::internal::FnOnce<I> for #name<#(#type_params,)* F>
where
F: ops::FnOnce(&mut #name<#(#type_params,)* ()>,I) -> O
{
type Output = O;
#[inline(always)]
fn call_once(mut self, args: I) -> Self::Output {
self.f()(&mut self.strip_f(), args)
}
}
impl<#(#type_params,)* F, I, O> self::internal::FnMut<I> for #name<#(#type_params,)* F>
where
F: ops::FnMut(&mut #name<#(#type_params,)* ()>,I) -> O
{
#[inline(always)]
fn call_mut(&mut self, args: I) -> Self::Output {
(&mut self.f())(self.strip_f_mut(), args)
}
}
},
Kind::FnOnce => quote! {
impl<#(#type_params,)* F, I, O> self::internal::FnOnce<I> for #name<#(#type_params,)* F>
where
F: ops::FnOnce(#name<#(#type_params,)* ()>,I) -> O
{
type Output = O;
#[inline(always)]
fn call_once(self, args: I) -> Self::Output {
self.f()(self.strip_f(), args)
}
}
},
};
let serialize_bounds = quote! { #(#type_params: Serialize,)* }.to_string();
let deserialize_bounds = quote! { #(#type_params: Deserialize<'de>,)* }.to_string();
Ok(quote! {
{
mod #impls_name {
#![allow(warnings)]
use ::serde_closure::{
internal::{self, is_phantom, to_phantom},
structs,
};
use self::internal::core::{
any::type_name,
clone::Clone,
cmp::{self, Eq, Ord, PartialEq, PartialOrd},
convert::From,
fmt::{self, Debug},
hash::{self, Hash},
marker::{Copy, PhantomData},
mem::{self, size_of, MaybeUninit},
ops,
option::Option::{self, Some},
};
use self::internal::serde::{Deserialize, Serialize};
use self::internal::std::{process::abort, string::{String, ToString}};
const SOURCE: &str = #source;
#[derive(Serialize, Deserialize)]
#[serde(
bound(serialize = #serialize_bounds),
bound(deserialize = #deserialize_bounds)
)]
pub struct #name<#(#type_params,)* F> {
#( pub #env_variables: #type_params, )*
#[serde(skip)]
__serde_closure_marker: PhantomData<F>
}
impl<#(#type_params,)*> #name<#(#type_params,)* ()> {
pub fn new(#( #env_variables: #type_params, )*) -> Self {
Self {
#( #env_variables ,)*
__serde_closure_marker: PhantomData
}
}
pub fn with_f<F1>(self, f: F1) -> #name<#(#type_params,)* F1> where F1: Copy {
if size_of::<F1>() != 0 {
abort();
}
#name {
#( #env_variables: self.#env_variables, )*
__serde_closure_marker: PhantomData
}
}
}
impl<#(#type_params,)* F> #name<#(#type_params,)* F> {
fn f(&self) -> F {
unsafe { MaybeUninit::uninit().assume_init() }
}
fn strip_f(self) -> #name<#(#type_params,)* ()> {
#name {
#( #env_variables: self.#env_variables, )*
__serde_closure_marker: PhantomData
}
}
fn strip_f_ref(&self) -> &#name<#(#type_params,)* ()> {
unsafe { &*(self as *const _ as *const _) }
}
fn strip_f_mut(&mut self) -> &mut #name<#(#type_params,)* ()> {
unsafe { &mut *(self as *mut _ as *mut _) }
}
}
impl<#(#type_params,)* F> Clone for #name<#(#type_params,)* F>
where
#(#type_params: Clone,)*
F: Clone,
{
fn clone(&self) -> Self {
Self {
#( #env_variables: self.#env_variables.clone(), )*
__serde_closure_marker: PhantomData,
}
}
}
impl<#(#type_params,)* F> Copy for #name<#(#type_params,)* F>
where
#(#type_params: Copy,)*
F: Copy,
{}
impl<#(#type_params,)* F> PartialEq for #name<#(#type_params,)* F>
where
#(#type_params: PartialEq,)*
{
fn eq(&self, other: &Self) -> bool {
#( self.#env_variables == self.#env_variables && )* true
}
}
impl<#(#type_params,)* F> Eq for #name<#(#type_params,)* F>
where
#(#type_params: Eq,)*
{}
impl<#(#type_params,)* F> Hash for #name<#(#type_params,)* F>
where
#(#type_params: Hash,)*
{
fn hash<H: hash::Hasher>(&self, state: &mut H) {
#( self.#env_variables.hash(state); )*
}
}
impl<#(#type_params,)* F> PartialOrd for #name<#(#type_params,)* F>
where
#(#type_params: PartialOrd,)*
{
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
let mut ord = Some(cmp::Ordering::Equal);
#(
ord = if let Some(cmp::Ordering::Equal) = ord {
self.#env_variables.partial_cmp(&other.#env_variables)
} else { ord };
)*
ord
}
}
impl<#(#type_params,)* F> Ord for #name<#(#type_params,)* F>
where
#(#type_params: Ord,)*
{
fn cmp(&self, other: &Self) -> cmp::Ordering {
let mut ord = cmp::Ordering::Equal;
#(
ord = if let cmp::Ordering::Equal = ord {
self.#env_variables.cmp(&other.#env_variables)
} else { ord };
)*
ord
}
}
impl<#(#type_params,)* F> Debug for #name<#(#type_params,)* F>
where
#(#type_params: Debug,)*
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let mut name = String::from(#name_string);
name.push('<');
name.push_str(type_name::<F>());
name.push_str(" at ");
name.push_str(file!());
name.push(':');
name.push_str(&line!().to_string());
name.push(':');
name.push_str(&column!().to_string());
name.push('>');
f.debug_struct(&name)
#( .field(#env_variable_names, &self.#env_variables) )*
.field("source", &SOURCE)
.finish()
}
}
#fn_impl
}
#[allow(warnings)]
{
#(let #env_variables = ::serde_closure::internal::a_variable;)*
}
#[allow(warnings)]
if false {
#(&#env_variables::<>;)*
}
#[allow(unused_mut)]
let mut #ret_name = #impls_name::#name::new(#env_capture);
let #env_types_name = ::serde_closure::internal::to_phantom(&#ret_name);
let closure =
#(#attrs)* #asyncness move |#env_name: #env_type, (#(#input_pats,)*): (#(#input_types,)*)| #output {
#[allow(warnings)]
if false {
::serde_closure::internal::is_phantom(& #env_deref, #env_types_name);
}
#env_deconstruct
#body
};
#[allow(warnings)]
{
if false {
let _ = closure(#ret_ref, loop {});
}
if false {
let ::serde_closure::internal::ZeroSizedAssertion = unsafe { ::serde_closure::internal::core::mem::transmute(closure) };
}
}
::serde_closure::structs::#name::internal_new(#ret_name.with_f(closure))
}
})
}
fn pat_to_type(pat: &Pat) -> Type {
match pat {
Pat::Type(pat_type) => {
if let Type::Infer(_) = *pat_type.ty {
pat_to_type(&*pat_type.pat)
} else {
(*pat_type.ty).clone()
}
}
Pat::Reference(PatReference {
and_token,
mutability,
pat,
..
}) => Type::Reference(TypeReference {
and_token: *and_token,
lifetime: None,
mutability: *mutability,
elem: Box::new(pat_to_type(pat)),
}),
_ => Type::Infer(TypeInfer {
underscore_token: Default::default(),
}),
}
}
struct State<'a> {
variables: HashSet<Ident>,
env_variables: &'a mut HashSet<Ident>,
not_env_variables: &'a mut HashSet<Ident>,
env_struct: bool,
deref: bool,
env_name: &'a Ident,
}
impl<'a> State<'a> {
fn new(
env_variables: &'a mut HashSet<Ident>, not_env_variables: &'a mut HashSet<Ident>,
env_struct: bool, deref: bool, env_name: &'a Ident,
) -> Self {
Self {
variables: HashSet::new(),
env_variables,
not_env_variables,
env_struct,
deref,
env_name,
}
}
fn enter_block(&mut self) -> State<'_> {
State {
variables: self.variables.clone(),
env_variables: self.env_variables,
not_env_variables: self.not_env_variables,
env_struct: self.env_struct,
deref: self.deref,
env_name: self.env_name,
}
}
fn pat(&mut self, pat: &mut Pat) {
match pat {
Pat::Ident(PatIdent { ident, subpat, .. }) => {
assert_ne!(ident, self.env_name);
let _ = self.variables.insert(ident.clone());
if let Some((_, subpat)) = subpat {
self.pat(subpat);
}
}
Pat::Box(PatBox { pat, .. })
| Pat::Reference(PatReference { pat, .. })
| Pat::Type(PatType { pat, .. }) => self.pat(&mut *pat),
Pat::Or(pat_or) => {
for case in &mut pat_or.cases {
self.pat(case)
}
}
Pat::Slice(PatSlice { elems, .. })
| Pat::Tuple(PatTuple { elems, .. })
| Pat::TupleStruct(PatTupleStruct {
pat: PatTuple { elems, .. },
..
}) => {
for elem in elems {
self.pat(elem)
}
}
Pat::Range(pat_range) => {
self.expr(&mut pat_range.lo, false);
self.expr(&mut pat_range.hi, false);
}
Pat::Struct(pat_struct) => {
for field in &mut pat_struct.fields {
self.pat(&mut *field.pat)
}
}
_ => (),
}
}
fn block(&mut self, stmts: &mut Vec<Stmt>) {
*stmts = take(stmts)
.into_iter()
.flat_map(|mut stmt| {
match &mut stmt {
Stmt::Local(Local { pat, init, .. }) => {
if let Some((_, expr)) = init {
self.expr(expr, false);
}
self.pat(pat);
}
Stmt::Expr(expr) | Stmt::Semi(expr, _) => {
self.expr(expr, false);
}
Stmt::Item(_) => (),
}
let not_env_variables = take(self.not_env_variables).into_iter();
let mut vec = Vec::with_capacity(2);
if not_env_variables.len() != 0 {
vec.push(
parse2(quote! { #[allow(warnings)] { #(use #not_env_variables;)* } })
.unwrap(),
);
}
vec.push(stmt);
vec
})
.collect();
}
fn expr(&mut self, expr: &mut Expr, is_func: bool) {
match expr {
Expr::Array(ExprArray { elems, .. }) | Expr::Tuple(ExprTuple { elems, .. }) => {
for elem in elems {
self.expr(elem, false);
}
}
Expr::Assign(ExprAssign { left, right, .. })
| Expr::AssignOp(ExprAssignOp { left, right, .. })
| Expr::Binary(ExprBinary { left, right, .. })
| Expr::Index(ExprIndex {
expr: left,
index: right,
..
})
| Expr::Repeat(ExprRepeat {
expr: left,
len: right,
..
}) => {
self.expr(left, false);
self.expr(right, false);
}
Expr::Async(ExprAsync { block, .. })
| Expr::Block(ExprBlock { block, .. })
| Expr::Loop(ExprLoop { body: block, .. })
| Expr::TryBlock(ExprTryBlock { block, .. })
| Expr::Unsafe(ExprUnsafe { block, .. }) => {
self.enter_block().block(&mut block.stmts);
}
Expr::Await(ExprAwait { base: expr, .. })
| Expr::Box(ExprBox { expr, .. })
| Expr::Break(ExprBreak {
expr: Some(expr), ..
})
| Expr::Cast(ExprCast { expr, .. })
| Expr::Field(ExprField { base: expr, .. })
| Expr::Group(ExprGroup { expr, .. })
| Expr::Paren(ExprParen { expr, .. })
| Expr::Reference(ExprReference { expr, .. })
| Expr::Return(ExprReturn {
expr: Some(expr), ..
})
| Expr::Try(ExprTry { expr, .. })
| Expr::Type(ExprType { expr, .. })
| Expr::Unary(ExprUnary { expr, .. })
| Expr::Yield(ExprYield {
expr: Some(expr), ..
}) => {
self.expr(expr, false);
}
Expr::Call(ExprCall { func, args, .. }) => {
match &**func {
Expr::Path(ExprPath { path, .. })
if path.leading_colon.is_none() && path.segments.len() == 1 =>
{
self.expr(func, true)
}
_ => self.expr(func, false),
}
for arg in args {
self.expr(arg, false);
}
}
Expr::Closure(ExprClosure { inputs, body, .. }) => {
let mut state = self.enter_block();
for input in inputs {
state.pat(input);
}
state.expr(body, false);
}
Expr::ForLoop(ExprForLoop {
pat, expr, body, ..
}) => {
self.expr(expr, false);
let mut state = self.enter_block();
state.pat(pat);
state.block(&mut body.stmts);
}
Expr::If(ExprIf {
cond,
then_branch,
else_branch,
..
}) => {
{
let mut state = self.enter_block();
state.expr(cond, false);
state.block(&mut then_branch.stmts);
}
if let Some((_, else_branch)) = else_branch {
self.expr(else_branch, false);
}
}
Expr::Let(ExprLet { pat, expr, .. }) => {
self.expr(expr, false);
self.pat(pat);
}
Expr::Match(ExprMatch { expr, arms, .. }) => {
self.expr(expr, false);
for Arm {
pat, guard, body, ..
} in arms
{
let mut state = self.enter_block();
state.pat(pat);
if let Some((_, guard)) = guard {
state.expr(guard, false);
}
state.expr(body, false);
}
}
Expr::MethodCall(ExprMethodCall { receiver, args, .. }) => {
self.expr(receiver, false);
for arg in args {
self.expr(arg, false);
}
}
Expr::Path(ExprPath { attrs, path, .. })
if path.leading_colon.is_none() && path.segments.len() == 1 =>
{
let path_segment = &path.segments.first().unwrap();
let ident = &path_segment.ident;
let has_path_arguments = !matches!(path_segment.arguments, PathArguments::None);
if !self.variables.contains(ident) {
if !(ident.to_string().chars().next().unwrap().is_uppercase()
|| is_func || has_path_arguments)
{
let _ = self.env_variables.insert(ident.clone());
if self.env_struct {
let mut a = Expr::Field(ExprField {
attrs: vec![],
base: Box::new(Expr::Path(ExprPath {
attrs: attrs.clone(),
qself: None,
path: Path {
leading_colon: None,
segments: iter::once(PathSegment {
ident: self.env_name.clone(),
arguments: PathArguments::None,
})
.collect(),
},
})),
dot_token: Default::default(),
member: Member::Named(ident.clone()),
});
if self.deref {
a = Expr::Unary(ExprUnary {
attrs: vec![],
op: UnOp::Deref(Default::default()),
expr: Box::new(a),
});
}
*expr = Expr::Paren(ExprParen {
attrs: vec![],
paren_token: Default::default(),
expr: Box::new(a),
});
}
} else {
let _ = self.not_env_variables.insert(ident.clone());
}
}
}
Expr::Range(ExprRange { from, to, .. }) => {
if let Some(from) = from {
self.expr(from, false);
}
if let Some(to) = to {
self.expr(to, false);
}
}
Expr::Struct(ExprStruct { fields, rest, .. }) => {
for FieldValue { expr, .. } in fields {
self.expr(expr, false);
}
if let Some(rest) = rest {
self.expr(rest, false);
}
}
Expr::While(ExprWhile { cond, body, .. }) => {
let mut state = self.enter_block();
state.expr(cond, false);
state.block(&mut body.stmts);
}
Expr::Macro(ExprMacro { ref mut mac, .. }) => {
let tokens = &mac.tokens;
if let Ok(expr) = parse2::<ExprCall>(quote! { self::abc(#tokens) }) {
let mut expr = Expr::Call(expr);
self.expr(&mut expr, false);
let expr = if let Expr::Call(expr) = expr {
expr
} else {
unreachable!()
};
mac.tokens = expr.args.to_token_stream();
} else {
panic!("See https://github.com/alecmocatta/serde_closure/issues/16");
}
}
_ => (),
}
}
}
#[inline(always)]
fn alpha_upper(u: u8) -> u8 {
assert!(u < 26);
u + b'A'
}
#[inline(always)]
fn alpha_lower(u: u8) -> u8 {
assert!(u < 26);
u + b'a'
}
const BUF_SIZE: usize = 64;
fn bijective_base(n: u64, base: u64, digits: impl Fn(u8) -> u8) -> String {
let mut buffer = [0_u8; BUF_SIZE];
let divided = successors(Some(n), |n| match n / base {
0 => None,
n => Some(n - 1),
});
#[allow(clippy::suspicious_map)]
let written = buffer
.iter_mut()
.rev()
.zip(divided)
.map(|(c, n)| *c = digits((n % base) as u8))
.count();
let index = BUF_SIZE - written;
str::from_utf8(&buffer[index..]).unwrap().to_owned()
}
#[test]
fn bijective_base_test() {
for i in 0..=26 + 26 * 26 + 26 * 26 * 26 {
let _ = bijective_base(i, 26, alpha_upper);
}
assert_eq!(
bijective_base(26 + 26 * 26 + 26 * 26 * 26, 26, alpha_upper),
"AAAA"
);
assert_eq!(
bijective_base(u64::max_value(), 3, alpha_upper),
"AAAABBABCBBABBAABCCAACCCACAACACCACCBAABBA"
);
assert_eq!(
bijective_base(u64::max_value(), 2, alpha_upper),
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB"
);
}