auto-import 0.1.0

Please do not use this.
Documentation
use proc_macro::TokenStream;
use std::collections::BTreeSet;
use std::io::{stderr, stdout, Write};
use std::process::{exit, Command};
use std::str::FromStr;
use std::sync::atomic::{AtomicBool, Ordering::Relaxed};

#[proc_macro]
pub fn magic(input: TokenStream) -> TokenStream {
    assert!(
        input.is_empty(),
        "auto_import::magic!() takes no arguments!"
    );

    static ONCE: AtomicBool = AtomicBool::new(false);

    if ONCE.swap(true, Relaxed) {
        panic!("don't call auto_import::magic!() more than once per crate!");
    }

    if let Ok(x) = std::env::var("autoimport") {
        return TokenStream::from_str(&x).unwrap();
    }

    let mut imports = BTreeSet::new();

    let mut attempts = 0;
    loop {
        attempts += 1;
        let mut change = false;
        let mut args = std::env::args_os();
        let out = Command::new(args.next().unwrap())
            .args(args.filter(|arg| {
                arg.to_str()
                    .map_or(true, |s| !s.starts_with("--error-format="))
            }))
            .arg("--error-format=json")
            .env(
                "autoimport",
                imports.iter().map(String::as_str).collect::<String>(),
            )
            .output()
            .unwrap();
        if out.status.success() {
            exit(0);
        }
        for line in std::str::from_utf8(&out.stderr)
            .unwrap()
            .lines()
            .filter(|l| l.starts_with('{'))
        {
            if let Ok(d) = json::parse(line) {
                for c in d["children"].members() {
                    let suggestion = c["spans"][0]["suggested_replacement"]
                        .as_str()
                        .unwrap_or_default();
                    if c["spans"][0]["text"].is_empty()
                        && suggestion.starts_with("use ")
                        && imports.insert(suggestion.to_string())
                    {
                        println!("\x1b[1;32m   Injecting\x1b[m {}", suggestion.trim());
                        change = true;
                    }
                }
            }
        }
        if !change || attempts == 10 {
            stderr().write_all(&out.stderr).unwrap();
            stdout().write_all(&out.stdout).unwrap();
            exit(out.status.code().unwrap_or(1));
        }
    }
}