From 1a3220f578db2774b9a800bf0341610a59f54271 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Sat, 4 Apr 2020 11:55:58 +0200 Subject: [PATCH] Fix busy-loop when proc-macro prints to stdout --- src/lib.rs | 9 +++++---- src/messages.rs | 27 ++++++++++++++++++++++++++- tests/test_samples.rs | 4 ++-- 3 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 84d64d8d..2dd6ea82 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -56,7 +56,8 @@ //! .spawn() //! .unwrap(); //! -//! for message in cargo_metadata::parse_messages(command.stdout.take().unwrap()) { +//! let reader = std::io::BufReader::new(command.stdout.take().unwrap()); +//! for message in cargo_metadata::Message::parse_stream(reader) { //! match message.unwrap() { //! Message::CompilerMessage(msg) => { //! println!("{:?}", msg); @@ -92,9 +93,9 @@ use semver::Version; pub use dependency::{Dependency, DependencyKind}; use diagnostic::Diagnostic; pub use errors::{Error, Result}; -pub use messages::{ - parse_messages, Artifact, ArtifactProfile, BuildScript, CompilerMessage, Message, -}; +pub use messages::{Artifact, ArtifactProfile, BuildScript, CompilerMessage, Message, MessageIter}; +#[allow(deprecated)] +pub use messages::parse_messages; mod dependency; pub mod diagnostic; diff --git a/src/messages.rs b/src/messages.rs index 06c1943d..a05405eb 100644 --- a/src/messages.rs +++ b/src/messages.rs @@ -1,7 +1,7 @@ use super::{Diagnostic, PackageId, Target}; use serde_json; use std::fmt; -use std::io::Read; +use std::io::{self, BufRead, Lines, Read}; use std::path::PathBuf; /// Profile settings used to determine which compiler flags to use for a @@ -100,18 +100,43 @@ pub enum Message { Unknown, } +impl Message { + /// Creates an iterator of Message from a Read outputting a stream of JSON + /// messages. For usage information, look at the top-level documentation. + pub fn parse_stream(input: R) -> MessageIter { + MessageIter { + lines: input.lines(), + } + } +} + impl fmt::Display for CompilerMessage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.message) } } +/// An iterator of Messages. +pub struct MessageIter { + lines: Lines, +} + +impl Iterator for MessageIter { + type Item = io::Result; + fn next(&mut self) -> Option { + let line = self.lines.next()?; + let message = line.and_then(|it| serde_json::from_str(&it).map_err(io::Error::from)); + Some(message) + } +} + /// An iterator of Message. type MessageIterator = serde_json::StreamDeserializer<'static, serde_json::de::IoRead, Message>; /// Creates an iterator of Message from a Read outputting a stream of JSON /// messages. For usage information, look at the top-level documentation. +#[deprecated(note = "Use Message::parse_stream instead")] pub fn parse_messages(input: R) -> MessageIterator { serde_json::Deserializer::from_reader(input).into_iter::() } diff --git a/tests/test_samples.rs b/tests/test_samples.rs index fb2c45f4..4123a92f 100644 --- a/tests/test_samples.rs +++ b/tests/test_samples.rs @@ -476,7 +476,7 @@ fn current_dir() { } #[test] -fn parse_messages_is_robust() { +fn parse_stream_is_robust() { // Proc macros can print stuff to stdout, which naturally breaks JSON messages. // Let's check that we don't die horribly in this case, and report an error. let json_output = r##"{"reason":"compiler-artifact","package_id":"chatty 0.1.0 (path+file:///chatty-macro/chatty)","target":{"kind":["proc-macro"],"crate_types":["proc-macro"],"name":"chatty","src_path":"/chatty-macro/chatty/src/lib.rs","edition":"2018","doctest":true},"profile":{"opt_level":"0","debuginfo":2,"debug_assertions":true,"overflow_checks":true,"test":false},"features":[],"filenames":["/chatty-macro/target/debug/deps/libchatty-f2adcff24cdf3bb2.so"],"executable":null,"fresh":false} @@ -485,7 +485,7 @@ Evil proc macro was here! "##; let mut good = 0; let mut bad = 0; - for m in cargo_metadata::parse_messages(json_output.as_bytes()) { + for m in cargo_metadata::Message::parse_stream(json_output.as_bytes()) { match m { Ok(_) => good += 1, Err(_) => bad += 1,