rust-analyzer-cli 0.2.0

A CLI tool built on top of rust-analyzer
use crate::lsp::types::{CheckDiagnosticItem, CheckResponse};
use anyhow::Result;
use serde_json::Value;
use std::path::Path;
use tokio::process::Command;

pub async fn run_cargo_check(workspace_root: &Path, target: Option<&str>) -> Result<CheckResponse> {
    let mut cmd = Command::new("cargo");
    cmd.arg("check").arg("--message-format=json");
    if let Some(tgt) = target {
        cmd.arg("--target").arg(tgt);
    }
    cmd.current_dir(workspace_root);

    let output = cmd.output().await?;
    let stdout_str = String::from_utf8_lossy(&output.stdout);

    let mut diagnostics = Vec::new();
    for line in stdout_str.lines() {
        if let Ok(v) = serde_json::from_str::<Value>(line) {
            if v.get("reason").and_then(|r| r.as_str()) == Some("compiler-message") {
                if let Some(msg_obj) = v.get("message") {
                    let level = msg_obj
                        .get("level")
                        .and_then(|l| l.as_str())
                        .unwrap_or("info")
                        .to_string();
                    let message = msg_obj
                        .get("message")
                        .and_then(|m| m.as_str())
                        .unwrap_or("")
                        .to_string();

                    let mut file = None;
                    let mut line_num = None;
                    let mut col_num = None;

                    if let Some(spans) = msg_obj.get("spans").and_then(|s| s.as_array()) {
                        if let Some(primary) = spans.iter().find(|s| s.get("is_primary").and_then(|p| p.as_bool()) == Some(true)) {
                            file = primary.get("file_name").and_then(|f| f.as_str()).map(String::from);
                            line_num = primary.get("line_start").and_then(|l| l.as_u64()).map(|n| n as u32);
                            col_num = primary.get("column_start").and_then(|c| c.as_u64()).map(|n| n as u32);
                        }
                    }

                    diagnostics.push(CheckDiagnosticItem {
                        message,
                        level,
                        file,
                        line: line_num,
                        col: col_num,
                    });
                }
            }
        }
    }

    Ok(CheckResponse {
        success: output.status.success(),
        diagnostics,
    })
}