syn_dissect_closure/
lib.rs1use std::{collections::HashSet, fmt};
2
3use quote::ToTokens;
4use syn::{parse_quote, token::Brace, Block, Expr, ExprBlock, ExprClosure, Ident, Pat, Stmt};
5
6mod state;
7use state::{EnvStruct, State};
8
9struct DissectedClosure {
10 env_variables: HashSet<Ident>,
11 closure: ExprClosure,
12}
13impl fmt::Debug for DissectedClosure {
14 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
15 f.debug_struct("DissectedClosure")
16 .field("env_variables", &self.env_variables)
17 .field("closure", &self.closure.to_token_stream())
18 .finish()
19 }
20}
21
22#[derive(Default)]
23struct ClosureDissector {
24 known_env: HashSet<Ident>,
25 known_not_env: HashSet<Ident>,
26 env: Option<EnvStruct>,
27}
28impl ClosureDissector {
29 fn dissect(mut self, mut closure: ExprClosure) -> DissectedClosure {
30 closure.body = Box::new(match *closure.body {
32 Expr::Block(block) => Expr::Block(block),
33 Expr::Tuple(tuple) if tuple.elems.is_empty() => Expr::Tuple(tuple),
35 expr => Expr::Block(ExprBlock {
36 attrs: vec![],
37 label: None,
38 block: Block {
39 brace_token: Brace::default(),
40 stmts: vec![Stmt::Expr(expr, None)],
41 },
42 }),
43 });
44
45 State::new(
46 &mut self.known_env,
47 &mut self.known_not_env,
48 self.env.as_ref(),
49 )
50 .expr_closure(&mut closure);
51
52 DissectedClosure {
53 env_variables: self.known_env,
54 closure,
55 }
56 }
57}
58
59pub fn split_env(closure: ExprClosure) -> (Expr, ExprClosure, Vec<Ident>) {
62 let mut dissected = ClosureDissector::default().dissect(closure);
63
64 let args = dissected.env_variables.iter().collect::<Vec<_>>();
65
66 let pat: Pat = parse_quote!((#(#args,)*));
67
68 dissected.closure.inputs.insert(0, pat);
69
70 let make_env: Expr = parse_quote! {
71 (#(#args,)*)
72 };
73
74 (
75 make_env,
76 dissected.closure,
77 args.into_iter().cloned().collect(),
78 )
79}