static_reflect_derive_internals/
utils.rs

1use proc_macro2::{TokenStream, Ident};
2use syn::{Item};
3use std::io::{Write};
4use std::fmt::Display;
5use quote::quote;
6
7pub fn is_derive_enabled(name: &str) -> bool {
8    match std::env::var(format!("DEBUG_DERIVE_{}", name)) {
9        Ok(s) => &*s == "1" || s.eq_ignore_ascii_case("true"),
10        Err(_) => false,
11    }
12}
13
14pub fn item_name(item: &Item) -> Box<dyn Display> {
15    let ident: &Ident = match *item {
16        Item::Fn(ref f) => &f.sig.ident,
17        Item::Static(ref s) => &s.ident,
18        Item::Struct(ref s) => &s.ident,
19        _ => return Box::new(format!("{}", quote!(item))) as Box<dyn Display>
20    };
21    Box::new(ident.clone()) as Box<dyn Display>
22}
23
24pub fn debug_proc_macro(macro_name: &str, input: &dyn Display, result: &TokenStream) {
25    if !is_derive_enabled(macro_name) { return }
26    let original = format!("{}", result);
27    match rustfmt_expr(&original) {
28        Ok(formatted) => {
29            eprintln!("{}!({}):", macro_name, input);
30            for line in formatted.lines() {
31                eprintln!("  {}", line);
32            }
33        },
34        Err(error) => {
35            eprintln!("{}!({}) caused rustfmt error:", macro_name, input);
36            for line in error.lines() {
37                eprintln!("  {}", line);
38            }
39            eprintln!("  original code: {}", original)
40        }
41    }
42}
43
44pub fn debug_derive(trait_name: &str, target: &dyn Display, result: &TokenStream) {
45    if !is_derive_enabled(trait_name) { return }
46    let original = format!("{}", result);
47    match rustfmt(&original) {
48        Ok(formatted) => {
49            eprintln!("derive({}) for {}:", trait_name, target);
50            for line in formatted.lines() {
51                eprintln!("  {}", line);
52            }
53        },
54        Err(error) => {
55            eprintln!("derive({}) for {} caused rustfmt error:", trait_name, target);
56            for line in error.lines() {
57                eprintln!("  {}", line);
58            }
59            eprintln!("  original code: {}", original)
60        }
61    }
62}
63
64pub fn rustfmt_expr(target: &str) -> Result<String, String> {
65    let dummy = format!(r#"fn expr() {{ {} }}"#, target);
66    rustfmt(&dummy)
67}
68
69pub fn rustfmt(target: &str) -> Result<String, String> {
70    use std::process::{Command, Stdio};
71    let mut child = match Command::new("rustfmt")
72        .stdin(Stdio::piped())
73        .stdout(Stdio::piped())
74        .stderr(Stdio::piped())
75        .spawn() {
76        Ok(child) => child,
77        Err(_) => {
78            /*
79             * Assume we just couldn't find the command.
80             * At this point invalid input couldn't have been
81             * the cause of our error
82             */
83            return Ok(target.into())
84        }
85    };
86    match child.stdin.as_mut().unwrap().write_all(target.as_bytes()).and_then(|()| {
87        let output = child.wait_with_output()?;
88        let utf_err = |cause: std::string::FromUtf8Error| {
89            std::io::Error::new(std::io::ErrorKind::InvalidData, cause)
90        };
91        let stdout = String::from_utf8(output.stdout)
92            .map_err(utf_err)?;
93        let stderr = String::from_utf8(output.stderr)
94            .map_err(utf_err)?;
95        Ok((output.status, stdout, stderr))
96    }) {
97        Ok((status, stdout, stderr)) => {
98            if status.success() {
99                Ok(stdout)
100            } else {
101                Err(stderr)
102            }
103        },
104        Err(e) => {
105            Err(format!("Unexpected IO error: {}", e))
106        }
107    }
108}