cargo_mate/
parser.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3#[derive(Debug, Deserialize, Serialize, Clone)]
4pub struct CargoMessage {
5    pub reason: String,
6    #[serde(flatten)]
7    pub data: MessageData,
8}
9#[derive(Debug, Deserialize, Serialize, Clone)]
10#[serde(untagged)]
11pub enum MessageData {
12    CompilerMessage(CompilerMessage),
13    BuildScriptExecuted(BuildScriptExecuted),
14    CompilerArtifact(CompilerArtifact),
15    Other(serde_json::Value),
16}
17#[derive(Debug, Deserialize, Serialize, Clone)]
18pub struct CompilerMessage {
19    pub message: DiagnosticMessage,
20    #[serde(default)]
21    pub package_id: String,
22    pub target: Option<Target>,
23}
24#[derive(Debug, Deserialize, Serialize, Clone)]
25pub struct DiagnosticMessage {
26    pub message: String,
27    pub code: Option<DiagnosticCode>,
28    pub level: String,
29    pub spans: Vec<DiagnosticSpan>,
30    pub children: Vec<DiagnosticMessage>,
31    pub rendered: Option<String>,
32}
33#[derive(Debug, Deserialize, Serialize, Clone)]
34pub struct DiagnosticCode {
35    pub code: String,
36    pub explanation: Option<String>,
37}
38#[derive(Debug, Deserialize, Serialize, Clone)]
39pub struct DiagnosticSpan {
40    pub file_name: String,
41    pub line_start: usize,
42    pub line_end: usize,
43    pub column_start: usize,
44    pub column_end: usize,
45    pub text: Vec<SpanText>,
46}
47#[derive(Debug, Deserialize, Serialize, Clone)]
48pub struct SpanText {
49    pub text: String,
50    pub highlight_start: usize,
51    pub highlight_end: usize,
52}
53#[derive(Debug, Deserialize, Serialize, Clone)]
54pub struct BuildScriptExecuted {
55    pub package_id: String,
56    pub linked_libs: Vec<String>,
57    pub linked_paths: Vec<String>,
58    pub cfgs: Vec<String>,
59    pub env: Vec<(String, String)>,
60}
61#[derive(Debug, Deserialize, Serialize, Clone)]
62pub struct CompilerArtifact {
63    pub package_id: String,
64    pub target: Target,
65    pub profile: ArtifactProfile,
66    pub features: Vec<String>,
67    pub filenames: Vec<String>,
68}
69#[derive(Debug, Deserialize, Serialize, Clone)]
70pub struct Target {
71    pub name: String,
72    pub kind: Vec<String>,
73    pub src_path: String,
74}
75#[derive(Debug, Deserialize, Serialize, Clone)]
76pub struct ArtifactProfile {
77    pub opt_level: String,
78    pub debuginfo: Option<u32>,
79    pub test: bool,
80}
81#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
82pub struct ParsedError {
83    pub code: String,
84    pub file: String,
85    pub line: usize,
86    pub message: String,
87}
88impl fmt::Display for ParsedError {
89    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90        write!(f, "[{}] {}:{} - {}", self.code, self.file, self.line, self.message)
91    }
92}
93#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
94pub struct ParsedWarning {
95    pub code: String,
96    pub file: String,
97    pub line: usize,
98    pub message: String,
99}
100impl fmt::Display for ParsedWarning {
101    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102        write!(f, "[{}] {}:{} - {}", self.code, self.file, self.line, self.message)
103    }
104}
105pub fn parse_cargo_message(line: &str) -> Option<CargoMessage> {
106    serde_json::from_str(line).ok()
107}
108pub fn format_error(msg: &DiagnosticMessage) -> ParsedError {
109    let code = msg
110        .code
111        .as_ref()
112        .map(|c| c.code.clone())
113        .unwrap_or_else(|| "unknown".to_string());
114    let (file, line) = if !msg.spans.is_empty() {
115        (msg.spans[0].file_name.clone(), msg.spans[0].line_start)
116    } else {
117        ("unknown".to_string(), 0)
118    };
119    ParsedError {
120        code,
121        file,
122        line,
123        message: msg.message.clone(),
124    }
125}
126pub fn format_warning(msg: &DiagnosticMessage) -> ParsedWarning {
127    let code = msg
128        .code
129        .as_ref()
130        .map(|c| c.code.clone())
131        .unwrap_or_else(|| "unknown".to_string());
132    let (file, line) = if !msg.spans.is_empty() {
133        (msg.spans[0].file_name.clone(), msg.spans[0].line_start)
134    } else {
135        ("unknown".to_string(), 0)
136    };
137    ParsedWarning {
138        code,
139        file,
140        line,
141        message: msg.message.clone(),
142    }
143}