1use std::{
14 borrow::Cow,
15 io::{self, BufRead, BufReader, Read, Write},
16 process::{Command, Stdio, exit},
17};
18
19use serde::Deserialize;
20
21#[derive(Deserialize)]
22struct CompilerMessageCode<'a> {
23 code: Cow<'a, str>,
24}
25
26#[derive(Deserialize)]
27struct CompilerMessage<'a> {
28 code: CompilerMessageCode<'a>,
29 rendered: Cow<'a, str>,
30 level: Cow<'a, str>,
31}
32
33#[derive(Deserialize)]
34#[serde(tag = "reason", rename_all = "kebab-case")]
35enum RustBuildMessage<'a> {
36 BuildFinished,
38 CompilerMessage {
39 message: CompilerMessage<'a>,
40 },
41}
42
43struct Args {
44 hidden_warnings: Vec<String>,
45 hide_all: bool,
46 disable_this_thing_entirely: bool,
47 args: Vec<String>,
48}
49
50fn parse_args(name: &str) -> Args {
51 let mut iter = std::env::args().peekable();
52 iter.next();
53 let mut args = vec![];
54 if iter.peek() == Some(&format!("{name}w")) {
55 iter.next();
56 }
57 let mut hide_all = false;
58 let mut hidden_warnings = vec![];
59 let mut disable_this_thing_entirely = false;
60 while let Some(x) = iter.next() {
61 if x == "--silence-warnings" || x == "-Wall" {
62 hide_all = true;
63 hidden_warnings.clear();
64 } else if x == "-W" || x == "--silence-warning" {
65 let Some(x) = iter.next() else {
66 eprintln!("usage: {x} <warning>");
67 exit(1);
68 };
69 if hide_all {
70 continue;
71 }
72 hidden_warnings.push(x);
73 } else if let Some(x) = x.strip_prefix("-W") {
74 if x.is_empty() {
75 eprintln!("usage: {x} <warning>");
76 exit(1);
77 };
78 if hide_all {
79 continue;
80 }
81 hidden_warnings.push(x.to_string());
82 } else if x == "--" {
83 args.push(x);
84 break;
85 } else if x == "--message-format" || x.starts_with("--message-format=") {
86 disable_this_thing_entirely = true;
87 args.push(x);
88 break;
89 } else {
90 args.push(x);
91 }
92 }
93 for x in iter {
94 args.push(x);
95 }
96 Args {
97 args,
98 hide_all,
99 hidden_warnings,
100 disable_this_thing_entirely,
101 }
102}
103
104#[doc(hidden)]
105pub fn run(name: &str) {
106 let args = parse_args(name);
107
108 if args.disable_this_thing_entirely {
109 let mut command = Command::new("cargo");
110 command.arg(name).args(args.args);
111 #[cfg(unix)]
112 {
113 use std::os::unix::process::CommandExt;
114 panic!("{:#}", command.exec());
115 }
116 #[allow(unreachable_code)]
117 if let Some(code) = command
118 .spawn()
119 .ok()
120 .and_then(|mut x| x.wait().ok())
121 .and_then(|x| x.code())
122 {
123 exit(code);
124 } else {
125 exit(-1);
126 }
127 }
128
129 let mut proc = Command::new("cargo")
130 .arg(name)
131 .arg("--message-format=json-diagnostic-rendered-ansi")
132 .args(args.args)
133 .stdout(Stdio::piped())
134 .spawn()
135 .unwrap();
136
137 let mut stdout = BufReader::new(proc.stdout.take().unwrap());
138
139 let mut line = String::new();
140 loop {
141 line.clear();
142 if stdout.read_line(&mut line).unwrap() == 0 {
143 break;
144 }
145 if line.starts_with('{') && line.trim_end().ends_with('}') {
146 if let Ok(x) = serde_json::from_str::<RustBuildMessage<'_>>(line.trim()) {
147 match x {
148 RustBuildMessage::BuildFinished => break,
149 RustBuildMessage::CompilerMessage { message } => 'a: {
150 'b: {
151 if message.level != "warning" {
152 break 'b;
153 }
154 if args.hide_all {
155 break 'a;
156 }
157 if args
158 .hidden_warnings
159 .iter()
160 .any(|x| x.as_str() == message.code.code.as_ref())
161 {
162 break 'a;
163 }
164 }
165 io::stdout().write_all(message.rendered.as_bytes()).ok();
166 }
167 }
168 }
169 } else {
170 io::stdout().write_all(line.as_bytes()).ok();
171 }
172 }
173
174 let mut buf = vec![0; 4096];
175 let mut cursor = 0;
176 let mut stdout = Some(stdout);
177 while stdout.is_some() || cursor > 0 {
178 if let Some(x) = &mut stdout
179 && cursor != buf.len()
180 {
181 let Ok(v) = x.read(&mut buf[cursor..]) else {
182 stdout.take();
183 continue;
184 };
185 if v == 0 {
186 stdout.take();
187 continue;
188 }
189 cursor += v;
190 }
191
192 if cursor > 0 {
193 let Ok(v) = io::stdout().write(&buf[..cursor]) else {
194 break;
195 };
196 if v == 0 {
197 break;
198 }
199 cursor -= v;
200 }
201 }
202
203 match proc.wait().map(|x| x.code()) {
204 Ok(Some(x)) => exit(x),
205 _ => exit(-1),
206 }
207}