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