1*d4726bddSHONG Yifan // Copyright 2020 The Bazel Authors. All rights reserved.
2*d4726bddSHONG Yifan //
3*d4726bddSHONG Yifan // Licensed under the Apache License, Version 2.0 (the "License");
4*d4726bddSHONG Yifan // you may not use this file except in compliance with the License.
5*d4726bddSHONG Yifan // You may obtain a copy of the License at
6*d4726bddSHONG Yifan //
7*d4726bddSHONG Yifan // http://www.apache.org/licenses/LICENSE-2.0
8*d4726bddSHONG Yifan //
9*d4726bddSHONG Yifan // Unless required by applicable law or agreed to in writing, software
10*d4726bddSHONG Yifan // distributed under the License is distributed on an "AS IS" BASIS,
11*d4726bddSHONG Yifan // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*d4726bddSHONG Yifan // See the License for the specific language governing permissions and
13*d4726bddSHONG Yifan // limitations under the License.
14*d4726bddSHONG Yifan
15*d4726bddSHONG Yifan use std::convert::{TryFrom, TryInto};
16*d4726bddSHONG Yifan
17*d4726bddSHONG Yifan use tinyjson::JsonValue;
18*d4726bddSHONG Yifan
19*d4726bddSHONG Yifan use crate::output::{LineOutput, LineResult};
20*d4726bddSHONG Yifan
21*d4726bddSHONG Yifan #[derive(Debug, Copy, Clone)]
22*d4726bddSHONG Yifan pub(crate) enum ErrorFormat {
23*d4726bddSHONG Yifan Json,
24*d4726bddSHONG Yifan Rendered,
25*d4726bddSHONG Yifan }
26*d4726bddSHONG Yifan
27*d4726bddSHONG Yifan impl Default for ErrorFormat {
default() -> Self28*d4726bddSHONG Yifan fn default() -> Self {
29*d4726bddSHONG Yifan Self::Rendered
30*d4726bddSHONG Yifan }
31*d4726bddSHONG Yifan }
32*d4726bddSHONG Yifan
get_key(value: &JsonValue, key: &str) -> Option<String>33*d4726bddSHONG Yifan fn get_key(value: &JsonValue, key: &str) -> Option<String> {
34*d4726bddSHONG Yifan if let JsonValue::Object(map) = value {
35*d4726bddSHONG Yifan if let JsonValue::String(s) = map.get(key)? {
36*d4726bddSHONG Yifan Some(s.clone())
37*d4726bddSHONG Yifan } else {
38*d4726bddSHONG Yifan None
39*d4726bddSHONG Yifan }
40*d4726bddSHONG Yifan } else {
41*d4726bddSHONG Yifan None
42*d4726bddSHONG Yifan }
43*d4726bddSHONG Yifan }
44*d4726bddSHONG Yifan
45*d4726bddSHONG Yifan #[derive(Debug)]
46*d4726bddSHONG Yifan enum RustcMessage {
47*d4726bddSHONG Yifan Emit(String),
48*d4726bddSHONG Yifan Message(String),
49*d4726bddSHONG Yifan }
50*d4726bddSHONG Yifan
51*d4726bddSHONG Yifan impl TryFrom<JsonValue> for RustcMessage {
52*d4726bddSHONG Yifan type Error = ();
try_from(val: JsonValue) -> Result<Self, Self::Error>53*d4726bddSHONG Yifan fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
54*d4726bddSHONG Yifan if let Some(emit) = get_key(&val, "emit") {
55*d4726bddSHONG Yifan return Ok(Self::Emit(emit));
56*d4726bddSHONG Yifan }
57*d4726bddSHONG Yifan if let Some(rendered) = get_key(&val, "rendered") {
58*d4726bddSHONG Yifan return Ok(Self::Message(rendered));
59*d4726bddSHONG Yifan }
60*d4726bddSHONG Yifan Err(())
61*d4726bddSHONG Yifan }
62*d4726bddSHONG Yifan }
63*d4726bddSHONG Yifan
64*d4726bddSHONG Yifan /// process_rustc_json takes an output line from rustc configured with
65*d4726bddSHONG Yifan /// --error-format=json, parses the json and returns the appropriate output
66*d4726bddSHONG Yifan /// according to the original --error-format supplied.
67*d4726bddSHONG Yifan /// Only messages are returned, emits are ignored.
68*d4726bddSHONG Yifan /// Retuns an errors if parsing json fails.
process_json(line: String, error_format: ErrorFormat) -> LineResult69*d4726bddSHONG Yifan pub(crate) fn process_json(line: String, error_format: ErrorFormat) -> LineResult {
70*d4726bddSHONG Yifan let parsed: JsonValue = line
71*d4726bddSHONG Yifan .parse()
72*d4726bddSHONG Yifan .map_err(|_| "error parsing rustc output as json".to_owned())?;
73*d4726bddSHONG Yifan Ok(match parsed.try_into() {
74*d4726bddSHONG Yifan Ok(RustcMessage::Message(rendered)) => {
75*d4726bddSHONG Yifan output_based_on_error_format(line, rendered, error_format)
76*d4726bddSHONG Yifan }
77*d4726bddSHONG Yifan _ => LineOutput::Skip,
78*d4726bddSHONG Yifan })
79*d4726bddSHONG Yifan }
80*d4726bddSHONG Yifan
81*d4726bddSHONG Yifan /// stop_on_rmeta_completion parses the json output of rustc in the same way
82*d4726bddSHONG Yifan /// process_rustc_json does. In addition, it will signal to stop when metadata
83*d4726bddSHONG Yifan /// is emitted so the compiler can be terminated.
84*d4726bddSHONG Yifan /// This is used to implement pipelining in rules_rust, please see
85*d4726bddSHONG Yifan /// https://internals.rust-lang.org/t/evaluating-pipelined-rustc-compilation/10199
86*d4726bddSHONG Yifan /// Retuns an error if parsing json fails.
87*d4726bddSHONG Yifan /// TODO: pass a function to handle the emit event and merge with process_json
stop_on_rmeta_completion( line: String, error_format: ErrorFormat, kill: &mut bool, ) -> LineResult88*d4726bddSHONG Yifan pub(crate) fn stop_on_rmeta_completion(
89*d4726bddSHONG Yifan line: String,
90*d4726bddSHONG Yifan error_format: ErrorFormat,
91*d4726bddSHONG Yifan kill: &mut bool,
92*d4726bddSHONG Yifan ) -> LineResult {
93*d4726bddSHONG Yifan let parsed: JsonValue = line
94*d4726bddSHONG Yifan .parse()
95*d4726bddSHONG Yifan .map_err(|_| "error parsing rustc output as json".to_owned())?;
96*d4726bddSHONG Yifan Ok(match parsed.try_into() {
97*d4726bddSHONG Yifan Ok(RustcMessage::Emit(emit)) if emit == "metadata" => {
98*d4726bddSHONG Yifan *kill = true;
99*d4726bddSHONG Yifan LineOutput::Terminate
100*d4726bddSHONG Yifan }
101*d4726bddSHONG Yifan Ok(RustcMessage::Message(rendered)) => {
102*d4726bddSHONG Yifan output_based_on_error_format(line, rendered, error_format)
103*d4726bddSHONG Yifan }
104*d4726bddSHONG Yifan _ => LineOutput::Skip,
105*d4726bddSHONG Yifan })
106*d4726bddSHONG Yifan }
107*d4726bddSHONG Yifan
output_based_on_error_format( line: String, rendered: String, error_format: ErrorFormat, ) -> LineOutput108*d4726bddSHONG Yifan fn output_based_on_error_format(
109*d4726bddSHONG Yifan line: String,
110*d4726bddSHONG Yifan rendered: String,
111*d4726bddSHONG Yifan error_format: ErrorFormat,
112*d4726bddSHONG Yifan ) -> LineOutput {
113*d4726bddSHONG Yifan match error_format {
114*d4726bddSHONG Yifan // If the output should be json, we just forward the messages as-is
115*d4726bddSHONG Yifan // using `line`.
116*d4726bddSHONG Yifan ErrorFormat::Json => LineOutput::Message(line),
117*d4726bddSHONG Yifan // Otherwise we return the rendered field.
118*d4726bddSHONG Yifan ErrorFormat::Rendered => LineOutput::Message(rendered),
119*d4726bddSHONG Yifan }
120*d4726bddSHONG Yifan }
121