use quote::quote_spanned;
use syn::punctuated::*;
use syn::spanned::Spanned;
use syn::visit_mut::*;
use syn::*;
#[derive(Debug)]
pub struct TransformIdents {
replaced_expression: Option<Expr>,
ascribed_type: Option<Type>,
debug: bool,
}
impl TransformIdents {
pub fn new() -> Self {
Self {
replaced_expression: None,
ascribed_type: None,
debug: false,
}
}
}
impl VisitMut for TransformIdents {
fn visit_expr_array_mut(&mut self, expr_array: &mut ExprArray) {
for mut el in Punctuated::pairs_mut(&mut expr_array.elems) {
let it = el.value_mut();
self.visit_expr_mut(it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replaced expression in <ExprArray>");
}
}
}
}
fn visit_expr_call_mut(&mut self, expr_call: &mut ExprCall) {
for mut el in Punctuated::pairs_mut(&mut expr_call.args) {
let it = el.value_mut();
self.visit_expr_mut(it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replaced expression in <ExprCall>");
}
}
}
}
#[allow(clippy::single_match)]
fn visit_expr_method_call_mut(&mut self, expr_method_call: &mut ExprMethodCall) {
match *expr_method_call.receiver {
Expr::Paren(ref mut paren) => match *paren.expr {
Expr::Range(ref mut range) => self.visit_expr_range_mut(range),
_ => {}
},
_ => {}
}
for mut el in Punctuated::pairs_mut(&mut expr_method_call.args) {
let it = el.value_mut();
self.visit_expr_mut(it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replaced expression in <ExprMethodCall>");
}
}
}
}
fn visit_expr_tuple_mut(&mut self, expr_tuple: &mut ExprTuple) {
for mut el in Punctuated::pairs_mut(&mut expr_tuple.elems) {
let it = el.value_mut();
self.visit_expr_mut(it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replace expression in <ExprTuple>");
}
}
}
}
fn visit_expr_binary_mut(&mut self, expr_binary: &mut ExprBinary) {
self.visit_expr_mut(&mut *expr_binary.left);
if let Some(expr) = self.replaced_expression.take() {
*expr_binary.left = expr;
if self.debug {
println!("Replace expression in <ExprBinary:Left>");
}
}
self.visit_expr_mut(&mut *expr_binary.right);
if let Some(expr) = self.replaced_expression.take() {
*expr_binary.right = expr;
if self.debug {
println!("Replace expression in <ExprBinary:Right>");
}
}
}
fn visit_expr_unary_mut(&mut self, expr_unary: &mut ExprUnary) {
self.visit_expr_mut(&mut *expr_unary.expr);
if let Some(expr) = self.replaced_expression.take() {
*expr_unary.expr = expr;
if self.debug {
println!("Replace expression in <ExprUnary>");
}
}
}
fn visit_expr_cast_mut(&mut self, expr_cast: &mut ExprCast) {
self.visit_expr_mut(&mut *expr_cast.expr);
if let Some(expr) = self.replaced_expression.take() {
*expr_cast.expr = expr;
if self.debug {
println!("Replace expression in <ExprCast>");
}
}
}
fn visit_expr_type_mut(&mut self, expr_type: &mut ExprType) {
self.ascribed_type = Some(*expr_type.ty.clone());
self.visit_expr_mut(&mut *expr_type.expr);
self.ascribed_type = None;
}
fn visit_expr_if_mut(&mut self, expr_if: &mut ExprIf) {
self.visit_expr_mut(&mut *expr_if.cond);
if let Some(expr) = self.replaced_expression.take() {
*expr_if.cond = expr;
if self.debug {
println!("Replace expression in <ExprIf:Cond>");
}
}
self.visit_block_mut(&mut expr_if.then_branch);
if let Some(ref mut it) = expr_if.else_branch {
self.visit_expr_mut(&mut *(it).1);
};
}
fn visit_stmt_mut(&mut self, stmt: &mut Stmt) {
match *stmt {
Stmt::Local(_) | Stmt::Item(_) | Stmt::Semi(_, _) => {
panic!("Only expressions are allowed in construct_with attribute, this includes inside if/else blocks");
}
Stmt::Expr(ref mut expr) => {
self.visit_expr_mut(expr);
if let Some(new_expr) = self.replaced_expression.take() {
*expr = new_expr;
if self.debug {
println!("Replace expression in <Stmt:Expr>");
}
}
}
}
}
fn visit_expr_range_mut(&mut self, expr_range: &mut ExprRange) {
if let Some(ref mut it) = expr_range.from {
self.visit_expr_mut(&mut **it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replace expression in <ExprRange:From>");
}
}
};
if let Some(ref mut it) = expr_range.to {
self.visit_expr_mut(&mut **it);
if let Some(expr) = self.replaced_expression.take() {
**it = expr;
if self.debug {
println!("Replace expression in <ExprRange:To>");
}
}
};
}
fn visit_expr_reference_mut(&mut self, expr_ref: &mut ExprReference) {
self.visit_expr_mut(&mut *expr_ref.expr);
if let Some(expr) = self.replaced_expression.take() {
*expr_ref.expr = expr;
if self.debug {
println!("Replace expression in <ExprReference>");
}
}
}
fn visit_expr_struct_mut(&mut self, expr_struct: &mut ExprStruct) {
for mut el in Punctuated::pairs_mut(&mut expr_struct.fields) {
let it = el.value_mut();
self.visit_field_value_mut(it)
}
if let Some(ref mut it) = expr_struct.rest {
self.visit_expr_mut(&mut **it)
};
}
fn visit_field_value_mut(&mut self, field_value: &mut FieldValue) {
self.visit_expr_mut(&mut field_value.expr);
if let Some(expr) = self.replaced_expression.take() {
field_value.expr = expr;
if field_value.colon_token.is_none() {
field_value.colon_token = Some(syn::token::Colon {
spans: [field_value.span()],
});
}
if self.debug {
println!("Replace expression in <FieldValue:Expr>");
}
}
}
fn visit_expr_paren_mut(&mut self, expr_paren: &mut ExprParen) {
self.visit_expr_mut(&mut *expr_paren.expr);
if let Some(expr) = self.replaced_expression.take() {
*expr_paren.expr = expr;
if self.debug {
println!("Replace expression in <ExprParen>");
}
}
}
fn visit_ident_mut(&mut self, ident: &mut Ident) {
if ident != "None" {
if self.debug {
println!("Found identifier: {:?}", ident);
}
let ident_as_string = ident.to_string();
let ts = if let Some(ty) = self.ascribed_type.take() {
if self.debug {
println!("Ascribed type: {:?}", ty);
}
let mut ty_is_str_ref = false;
if let Type::Reference(ref ty_ref) = ty {
if let Type::Path(ref path) = *ty_ref.elem {
ty_is_str_ref = path.path.is_ident("str");
}
}
if ty_is_str_ref && self.debug {
println!("Ascribed type is &str");
}
if ty_is_str_ref {
quote_spanned!(ident.span()=> extractor.extract(#ident_as_string)?)
} else {
quote_spanned!(ident.span()=> extractor.extract(#ident_as_string)?.parse::<#ty>()?)
}
} else {
quote_spanned!(ident.span()=> extractor.extract(#ident_as_string)?.parse()?)
};
let expr: Expr = parse2(ts).unwrap();
self.replaced_expression = Some(expr);
}
}
}