Skip to content

Commit e220885

Browse files
authored
Use json output format in cargo if set in scarb_ui (#1151)
commit-id:fd15f5ab --- **Stack**: - #1161 - #1159 - #1157 - #1143 - #1155 - #1151⚠️ *Part of a stack created by [spr](https://github.com/ejoffe/spr). Do not merge manually using the UI - doing so may have unexpected results.*
1 parent e7bf5da commit e220885

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

scarb/src/compiler/plugin/proc_macro/compilation.rs

+60-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ use crate::process::exec_piping;
66
use anyhow::Result;
77
use camino::Utf8PathBuf;
88
use libloading::library_filename;
9+
use scarb_ui::{Message, OutputFormat};
10+
use serde::{Serialize, Serializer};
11+
use serde_json::value::RawValue;
12+
use std::fmt::Display;
913
use std::process::Command;
1014
use tracing::trace_span;
1115

@@ -62,6 +66,7 @@ fn run_cargo(action: CargoAction, package: &Package, ws: &Workspace<'_>) -> Resu
6266
let cmd = CargoCommand {
6367
action,
6468
current_dir: package.root().to_path_buf(),
69+
output_format: ws.config().ui().output_format(),
6570
target_dir: package
6671
.target_path(ws.config())
6772
.path_unchecked()
@@ -83,9 +88,33 @@ enum CargoAction {
8388
struct CargoCommand {
8489
current_dir: Utf8PathBuf,
8590
target_dir: Utf8PathBuf,
91+
output_format: OutputFormat,
8692
action: CargoAction,
8793
}
8894

95+
enum CargoOutputFormat {
96+
Human,
97+
Json,
98+
}
99+
100+
impl Display for CargoOutputFormat {
101+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102+
match self {
103+
CargoOutputFormat::Human => write!(f, "human"),
104+
CargoOutputFormat::Json => write!(f, "json"),
105+
}
106+
}
107+
}
108+
109+
impl From<OutputFormat> for CargoOutputFormat {
110+
fn from(format: OutputFormat) -> Self {
111+
match format {
112+
OutputFormat::Text => CargoOutputFormat::Human,
113+
OutputFormat::Json => CargoOutputFormat::Json,
114+
}
115+
}
116+
}
117+
89118
impl From<CargoCommand> for Command {
90119
fn from(args: CargoCommand) -> Self {
91120
let mut cmd = Command::new("cargo");
@@ -99,6 +128,9 @@ impl From<CargoCommand> for Command {
99128
CargoAction::Fetch => (),
100129
_ => {
101130
cmd.arg("--release");
131+
cmd.arg("--message-format");
132+
let output_format: CargoOutputFormat = args.output_format.into();
133+
cmd.arg(output_format.to_string());
102134
cmd.arg("--target-dir");
103135
cmd.arg(args.target_dir);
104136
}
@@ -111,7 +143,33 @@ fn exec(cmd: &mut Command, config: &Config) -> Result<()> {
111143
exec_piping(
112144
cmd,
113145
config,
114-
|line: &str| config.ui().print(line),
115-
|line: &str| config.ui().print(line),
146+
|line: &str| config.ui().print(PipedText::new(line)),
147+
|line: &str| config.ui().print(PipedText::new(line)),
116148
)
117149
}
150+
151+
/// This message can be used for piped text from subprocesses.
152+
///
153+
/// It accepts either a string or a JSON string.
154+
/// If the input is a JSON string, it can be serialized as a structured message.
155+
/// Otherwise, the structured message will be skipped.
156+
pub struct PipedText(String);
157+
158+
impl PipedText {
159+
pub fn new(text: impl Into<String>) -> Self {
160+
Self(text.into())
161+
}
162+
}
163+
164+
impl Message for PipedText {
165+
fn text(self) -> String {
166+
self.0
167+
}
168+
169+
fn structured<S: Serializer>(self, ser: S) -> Result<S::Ok, S::Error> {
170+
match serde_json::from_str::<&RawValue>(self.0.as_str()) {
171+
Ok(value) => value.serialize(ser),
172+
Err(_e) => Self::skip_structured(ser),
173+
}
174+
}
175+
}

scarb/tests/build_cairo_plugin.rs

+34
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,40 @@ fn resolve_fetched_plugins() {
165165
assert!(t.child("Cargo.lock").exists())
166166
}
167167

168+
#[test]
169+
fn can_use_json_output() {
170+
let t = TempDir::new().unwrap();
171+
simple_project(&t);
172+
let output = Scarb::quick_snapbox()
173+
.arg("--json")
174+
.arg("check")
175+
// Disable colors in Cargo output.
176+
.env("CARGO_TERM_COLOR", "never")
177+
.current_dir(&t)
178+
.output()
179+
.unwrap();
180+
assert!(
181+
output.status.success(),
182+
"{}",
183+
String::from_utf8_lossy(&output.stderr)
184+
);
185+
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
186+
let lines = stdout.lines().map(ToString::to_string).collect::<Vec<_>>();
187+
let (first, lines) = lines.split_first().unwrap();
188+
assert_matches(
189+
r#"{"status":"checking","message":"hello v1.0.0 ([..]Scarb.toml)"}"#,
190+
first,
191+
);
192+
let (last, lines) = lines.split_last().unwrap();
193+
assert_matches(
194+
r#"{"status":"finished","message":"checking release target(s) in [..]"}"#,
195+
last,
196+
);
197+
// Line from Cargo.
198+
let (last, _lines) = lines.split_last().unwrap();
199+
assert_matches(r#"{"reason":"build-finished","success":true}"#, last);
200+
}
201+
168202
#[test]
169203
fn compile_cairo_plugin_with_lib_target() {
170204
let t = TempDir::new().unwrap();

utils/scarb-ui/src/message.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,11 @@ pub trait Message {
4242
where
4343
Self: Sized,
4444
{
45-
// Silence unused warning without using underscore in variable name,
46-
// so that it will not be populated by editors.
47-
let _ = ser;
45+
Self::skip_structured(ser)
46+
}
47+
48+
#[doc(hidden)]
49+
fn skip_structured<S: Serializer>(_ser: S) -> Result<S::Ok, S::Error> {
4850
Err(serde::ser::Error::custom(JSON_SKIP_MESSAGE))
4951
}
5052

0 commit comments

Comments
 (0)