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}