Skip to content

Commit 835a06b

Browse files
committed
writer error contains filename
1 parent 0ecae3f commit 835a06b

File tree

5 files changed

+172
-131
lines changed

5 files changed

+172
-131
lines changed

bear/src/modes/execution.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use crate::args::BuildCommand;
44
use crate::intercept;
55
use crate::intercept::supervise::SuperviseError;
66
use crate::intercept::tcp::ReceivingError;
7-
use crate::output::FormatError;
7+
use crate::output::WriterError;
88
use crossbeam_channel::{bounded, unbounded, Receiver};
99
use std::process::{ExitCode, ExitStatus};
1010
use std::sync::Arc;
@@ -28,7 +28,7 @@ pub trait Consumer: Send {
2828
/// # Returns
2929
/// * `Ok(())` - All items were successfully processed
3030
/// * `Err(FormatError)` - An error occurred during processing
31-
fn consume(self: Box<Self>, receiver: Receiver<intercept::Event>) -> Result<(), FormatError>;
31+
fn consume(self: Box<Self>, receiver: Receiver<intercept::Event>) -> Result<(), WriterError>;
3232
}
3333

3434
/// A trait for producing events to a channel-based stream.
@@ -235,7 +235,7 @@ pub enum RuntimeError {
235235
#[error("Producer error: {0}")]
236236
Producer(#[from] ReceivingError),
237237
#[error("Consumer error: {0}")]
238-
Consumer(#[from] FormatError),
238+
Consumer(#[from] WriterError),
239239
#[error("Executor error: {0}")]
240240
Executor(#[from] SuperviseError),
241241
#[error("Thread error: {0}")]
@@ -246,6 +246,7 @@ pub enum RuntimeError {
246246
mod tests {
247247
use super::*;
248248
use crate::intercept::Event;
249+
use crate::output::{SerializationError, WriterError};
249250
use std::collections::HashMap;
250251
use std::sync::{Arc, Mutex};
251252
use std::time::Duration;
@@ -465,10 +466,12 @@ mod tests {
465466
});
466467

467468
let mut consumer_mock = MockConsumer::new();
468-
consumer_mock
469-
.expect_consume()
470-
.times(1)
471-
.returning(|_| Err(FormatError::Io(std::io::Error::other("Test failure"))));
469+
consumer_mock.expect_consume().times(1).returning(|_| {
470+
Err(WriterError::Io(
471+
std::path::PathBuf::new(),
472+
SerializationError::Io(std::io::Error::other("Test failure")),
473+
))
474+
});
472475

473476
let replayer = Replayer::new(Box::new(producer_mock), Box::new(consumer_mock));
474477
let result = replayer.run();
@@ -632,10 +635,12 @@ mod tests {
632635
let producer_mock = Arc::new(MockCancellableProducer::new(events));
633636

634637
let mut consumer_mock = MockConsumer::new();
635-
consumer_mock
636-
.expect_consume()
637-
.times(1)
638-
.returning(|_| Err(FormatError::Io(std::io::Error::other("Test failure"))));
638+
consumer_mock.expect_consume().times(1).returning(|_| {
639+
Err(WriterError::Io(
640+
std::path::PathBuf::new(),
641+
SerializationError::Io(std::io::Error::other("Test failure")),
642+
))
643+
});
639644

640645
let mut executor_mock = MockExecutor::new();
641646
executor_mock

bear/src/modes/mod.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,9 @@ mod impls {
124124
use crate::intercept::environment;
125125
use crate::intercept::supervise::SuperviseError;
126126
use crate::intercept::tcp::{CollectorOnTcp, ReceivingError};
127-
use crate::output::{ExecutionEventDatabase, FileFormat, FormatError, WriterCreationError};
127+
use crate::output::{
128+
ExecutionEventDatabase, SerializationFormat, WriterCreationError, WriterError,
129+
};
128130
use crate::{args, config, intercept, output, semantic};
129131
use crossbeam_channel::{Receiver, Sender};
130132
use std::process::ExitStatus;
@@ -221,26 +223,29 @@ mod impls {
221223
/// The raw event writer will write the intercepted events as they are observed
222224
/// without any transformation. This can be later replayed to analyze the build.
223225
pub(super) struct RawEventWriter {
226+
path: path::PathBuf,
224227
destination: io::BufWriter<fs::File>,
225228
}
226229

227230
impl RawEventWriter {
228231
/// Create a new raw event writer.
229232
///
230233
/// This writer will write the intercepted events to a file in a raw format.
231-
pub(super) fn create(file_name: &str) -> Result<Self, WriterCreationError> {
232-
let destination = fs::File::create(file_name)
234+
pub(super) fn create(filename: &str) -> Result<Self, WriterCreationError> {
235+
let path = path::PathBuf::from(filename);
236+
let destination = fs::File::create(filename)
233237
.map(io::BufWriter::new)
234-
.map_err(WriterCreationError::Io)?;
238+
.map_err(|err| WriterCreationError::Io(path.clone(), err))?;
235239

236-
Ok(Self { destination })
240+
Ok(Self { path, destination })
237241
}
238242
}
239243

240244
impl execution::Consumer for RawEventWriter {
241245
/// Using existing file format, write the intercepted events to the output file.
242-
fn consume(self: Box<Self>, events: Receiver<intercept::Event>) -> Result<(), FormatError> {
246+
fn consume(self: Box<Self>, events: Receiver<intercept::Event>) -> Result<(), WriterError> {
243247
ExecutionEventDatabase::write(self.destination, events.into_iter())
248+
.map_err(|err| WriterError::Io(self.path.clone(), err))
244249
}
245250
}
246251

@@ -278,7 +283,7 @@ mod impls {
278283
impl execution::Consumer for SemanticEventWriter {
279284
/// Consume the intercepted events, and transform them into semantic events,
280285
/// and write them into the target file (with the right format).
281-
fn consume(self: Box<Self>, events: Receiver<intercept::Event>) -> Result<(), FormatError> {
286+
fn consume(self: Box<Self>, events: Receiver<intercept::Event>) -> Result<(), WriterError> {
282287
// Transform and log the events to semantics.
283288
let semantics = events
284289
.into_iter()

bear/src/output/formats.rs

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,25 @@
77
//! - The semantic database format. (Internal format of this project.)
88
//! - The execution event database format. (Internal format of this project.)
99
10-
use super::{json, FormatError};
10+
use super::json;
1111
use crate::{intercept, semantic, semantic::clang};
1212
use serde_json::de::IoRead;
1313
use serde_json::StreamDeserializer;
14-
use std::io;
14+
use thiserror::Error;
1515

1616
/// The trait represents a file format that can be written to and read from.
1717
///
1818
/// The file format in this project is usually a sequence of values. This trait
1919
/// provides a type-independent abstraction over the file format.
20-
pub trait FileFormat<T> {
21-
fn write(_: impl io::Write, _: impl Iterator<Item = T>) -> Result<(), FormatError>;
20+
pub trait SerializationFormat<T> {
21+
fn write(_: impl std::io::Write, _: impl Iterator<Item = T>) -> Result<(), SerializationError>;
2222

23-
fn read(_: impl io::Read) -> impl Iterator<Item = Result<T, FormatError>>;
23+
fn read(_: impl std::io::Read) -> impl Iterator<Item = Result<T, SerializationError>>;
2424

2525
/// Reads the entries from the file and ignores any errors.
2626
/// This is not always feasible, when the file format is strict.
2727
fn read_and_ignore(
28-
reader: impl io::Read,
28+
reader: impl std::io::Read,
2929
message_writer: impl Fn(&str),
3030
) -> impl Iterator<Item = T> {
3131
Self::read(reader).filter_map(move |result| match result {
@@ -38,6 +38,17 @@ pub trait FileFormat<T> {
3838
}
3939
}
4040

41+
/// Represents errors that can occur while working with file formats.
42+
#[derive(Debug, Error)]
43+
pub enum SerializationError {
44+
#[error("Generic IO error: {0}")]
45+
Io(#[from] std::io::Error),
46+
#[error("Format syntax error: {0}")]
47+
Syntax(#[from] serde_json::Error),
48+
#[error("Format semantic error: {0}")]
49+
Semantic(#[from] clang::EntryError),
50+
}
51+
4152
/// The type represents a JSON compilation database format.
4253
///
4354
/// The format is a JSON array format, which is a sequence of JSON objects
@@ -49,28 +60,30 @@ pub trait FileFormat<T> {
4960
/// https://clang.llvm.org/docs/JSONCompilationDatabase.html
5061
pub struct JsonCompilationDatabase;
5162

52-
impl FileFormat<clang::Entry> for JsonCompilationDatabase {
63+
impl SerializationFormat<clang::Entry> for JsonCompilationDatabase {
5364
fn write(
54-
writer: impl io::Write,
65+
writer: impl std::io::Write,
5566
entries: impl Iterator<Item = clang::Entry>,
56-
) -> Result<(), FormatError> {
67+
) -> Result<(), SerializationError> {
5768
json::serialize_result_seq(
5869
writer,
5970
// Ensure only valid entries are serialized.
6071
entries.map(|entry| match entry.validate() {
6172
Ok(_) => Ok(entry),
62-
Err(err) => Err(FormatError::Semantic(err)),
73+
Err(err) => Err(SerializationError::Semantic(err)),
6374
}),
6475
)
6576
}
6677

67-
fn read(reader: impl io::Read) -> impl Iterator<Item = Result<clang::Entry, FormatError>> {
78+
fn read(
79+
reader: impl std::io::Read,
80+
) -> impl Iterator<Item = Result<clang::Entry, SerializationError>> {
6881
json::deserialize_seq(reader).map(|res| {
69-
res.map_err(FormatError::Syntax)
82+
res.map_err(SerializationError::Syntax)
7083
// Ensure only valid entries are returned.
7184
.and_then(|entry: clang::Entry| match entry.validate() {
7285
Ok(_) => Ok(entry),
73-
Err(err) => Err(FormatError::Semantic(err)),
86+
Err(err) => Err(SerializationError::Semantic(err)),
7487
})
7588
})
7689
}
@@ -86,14 +99,16 @@ impl FileFormat<clang::Entry> for JsonCompilationDatabase {
8699
/// The output format is not stable and may change in future versions.
87100
pub struct JsonSemanticDatabase;
88101

89-
impl FileFormat<semantic::Command> for JsonSemanticDatabase {
102+
impl SerializationFormat<semantic::Command> for JsonSemanticDatabase {
90103
fn write(
91-
writer: impl io::Write,
104+
writer: impl std::io::Write,
92105
entries: impl Iterator<Item = semantic::Command>,
93-
) -> Result<(), FormatError> {
94-
json::serialize_seq(writer, entries).map_err(FormatError::Syntax)
106+
) -> Result<(), SerializationError> {
107+
json::serialize_seq(writer, entries).map_err(SerializationError::Syntax)
95108
}
96-
fn read(_: impl io::Read) -> impl Iterator<Item = Result<semantic::Command, FormatError>> {
109+
fn read(
110+
_: impl std::io::Read,
111+
) -> impl Iterator<Item = Result<semantic::Command, SerializationError>> {
97112
// Not implemented! (No reader for the semantic output in this project.)
98113
std::iter::empty()
99114
}
@@ -108,22 +123,24 @@ impl FileFormat<semantic::Command> for JsonSemanticDatabase {
108123
/// The output format is not stable and may change in future versions.
109124
pub struct ExecutionEventDatabase;
110125

111-
impl FileFormat<intercept::Event> for ExecutionEventDatabase {
126+
impl SerializationFormat<intercept::Event> for ExecutionEventDatabase {
112127
fn write(
113-
writer: impl io::Write,
128+
writer: impl std::io::Write,
114129
events: impl Iterator<Item = intercept::Event>,
115-
) -> Result<(), FormatError> {
130+
) -> Result<(), SerializationError> {
116131
let mut writer = writer;
117132
for event in events {
118-
serde_json::to_writer(&mut writer, &event).map_err(FormatError::Syntax)?;
119-
writer.write_all(b"\n").map_err(FormatError::Io)?;
133+
serde_json::to_writer(&mut writer, &event).map_err(SerializationError::Syntax)?;
134+
writer.write_all(b"\n").map_err(SerializationError::Io)?;
120135
}
121136
Ok(())
122137
}
123138

124-
fn read(reader: impl io::Read) -> impl Iterator<Item = Result<intercept::Event, FormatError>> {
139+
fn read(
140+
reader: impl std::io::Read,
141+
) -> impl Iterator<Item = Result<intercept::Event, SerializationError>> {
125142
let stream = StreamDeserializer::new(IoRead::new(reader));
126-
stream.map(|value| value.map_err(FormatError::Syntax))
143+
stream.map(|value| value.map_err(SerializationError::Syntax))
127144
}
128145
}
129146

@@ -132,15 +149,15 @@ mod test {
132149
mod compilation_database {
133150
use super::super::semantic::clang::{Entry, EntryError};
134151
use super::super::JsonCompilationDatabase as Sut;
135-
use super::super::{FileFormat, FormatError};
152+
use super::super::{SerializationError, SerializationFormat};
136153
use serde_json::error::Category;
137154
use serde_json::json;
138155
use std::io::{Cursor, Seek, SeekFrom};
139156

140157
macro_rules! assert_json_error {
141158
($x:expr) => {
142159
match $x {
143-
Some(Err(FormatError::Syntax(error))) => {
160+
Some(Err(SerializationError::Syntax(error))) => {
144161
assert_eq!(error.classify(), Category::Data)
145162
}
146163
_ => assert!(false, "shout be JSON error"),
@@ -151,7 +168,7 @@ mod test {
151168
macro_rules! assert_format_error {
152169
($x:expr) => {
153170
assert!(
154-
matches!($x, Some(Err(FormatError::Semantic(_)))),
171+
matches!($x, Some(Err(SerializationError::Semantic(_)))),
155172
"should be format error"
156173
);
157174
};
@@ -226,7 +243,7 @@ mod test {
226243
let result = Sut::write(&mut buffer, vec![entry].into_iter());
227244

228245
assert!(result.is_err());
229-
assert!(matches!(result, Err(FormatError::Semantic(_))));
246+
assert!(matches!(result, Err(SerializationError::Semantic(_))));
230247
}
231248

232249
fn expected_values_with_arguments() -> Vec<Entry> {
@@ -316,7 +333,7 @@ mod test {
316333
}
317334

318335
#[test]
319-
fn save_with_array_command_syntax() -> Result<(), FormatError> {
336+
fn save_with_array_command_syntax() -> Result<(), SerializationError> {
320337
let input = expected_values_with_arguments();
321338

322339
// Create fake "file"
@@ -434,7 +451,7 @@ mod test {
434451
}
435452

436453
#[test]
437-
fn save_quoted_with_array_command_syntax() -> Result<(), FormatError> {
454+
fn save_quoted_with_array_command_syntax() -> Result<(), SerializationError> {
438455
let input = expected_quoted_values_with_argument();
439456

440457
// Create fake "file"
@@ -454,7 +471,7 @@ mod test {
454471

455472
mod execution_events {
456473
use super::super::ExecutionEventDatabase as Sut;
457-
use super::super::FileFormat;
474+
use super::super::SerializationFormat;
458475
use crate::intercept::Event;
459476
use serde_json::json;
460477
use std::collections::HashMap;

bear/src/output/mod.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use writers::{
2222
};
2323

2424
// Re-export types for convenience.
25-
pub use formats::{ExecutionEventDatabase, FileFormat};
25+
pub use formats::{ExecutionEventDatabase, SerializationError, SerializationFormat};
2626

2727
/// Represents the output writer, which can handle different types of outputs.
2828
///
@@ -59,7 +59,7 @@ impl TryFrom<(&args::BuildSemantic, &config::Output)> for OutputWriter {
5959
let atomic_writer =
6060
AtomicClangOutputWriter::new(unique_writer, &temp_file_name, final_file_name);
6161
let append_writer =
62-
AppendClangOutputWriter::new(atomic_writer, args.append, final_file_name);
62+
AppendClangOutputWriter::new(atomic_writer, final_file_name, args.append);
6363
let formatted_writer =
6464
ConverterClangOutputWriter::new(append_writer, &format.entry);
6565

@@ -74,34 +74,30 @@ impl TryFrom<(&args::BuildSemantic, &config::Output)> for OutputWriter {
7474
}
7575
}
7676

77-
/// Represents errors that can occur while creating an output writer.
78-
#[derive(Error, Debug)]
79-
pub enum WriterCreationError {
80-
#[error("Failed to create the output writer: {0}")]
81-
Io(#[from] std::io::Error),
82-
#[error("Failed to configure the output writer: {0}")]
83-
Configuration(String),
84-
}
85-
8677
impl OutputWriter {
8778
pub fn write(
8879
self,
8980
semantics: impl Iterator<Item = semantic::Command>,
90-
) -> Result<(), FormatError> {
81+
) -> Result<(), WriterError> {
9182
match self {
9283
Self::Clang(writer) => writer.write(semantics),
9384
Self::Semantic(writer) => writer.write(semantics),
9485
}
9586
}
9687
}
9788

98-
/// Represents errors that can occur while working with file formats.
99-
#[derive(Debug, Error)]
100-
pub enum FormatError {
101-
#[error("Generic IO error: {0}")]
102-
Io(#[from] std::io::Error),
103-
#[error("Format syntax error: {0}")]
104-
Syntax(#[from] serde_json::Error),
105-
#[error("Format semantic error: {0}")]
106-
Semantic(#[from] semantic::clang::EntryError),
89+
/// Represents errors that can occur while creating an output writer.
90+
#[derive(Error, Debug)]
91+
pub enum WriterCreationError {
92+
#[error("Failed to create the output writer {0}: {1}")]
93+
Io(std::path::PathBuf, std::io::Error),
94+
#[error("Failed to configure the output writer: {0}")]
95+
Configuration(String),
96+
}
97+
98+
/// Represents errors that can occur while writing output.
99+
#[derive(Error, Debug)]
100+
pub enum WriterError {
101+
#[error("Serialization error {0}: {1}")]
102+
Io(std::path::PathBuf, SerializationError),
107103
}

0 commit comments

Comments
 (0)