Skip to content

Commit 3b6effb

Browse files
committed
rust: output writer refactoring
1 parent 79251ba commit 3b6effb

File tree

1 file changed

+70
-87
lines changed

1 file changed

+70
-87
lines changed

rust/bear/src/output/mod.rs

Lines changed: 70 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,6 @@ use anyhow::Context;
1111
use formats::{FileFormat, JsonCompilationDatabase, JsonSemanticDatabase};
1212
use std::{fs, io, path};
1313

14-
/// The trait represents a writer for iterator type `T`.
15-
///
16-
/// This trait is implemented by types that can consume an iterator of type `T`
17-
/// and write its elements to some output. The writing process may succeed or fail,
18-
/// returning either `()` on success or an error.
19-
pub trait IteratorWriter<T> {
20-
/// Writes the iterator as a sequence of elements.
21-
/// It consumes the iterator and returns either a nothing or an error.
22-
fn write_array(self, _: impl Iterator<Item = T>) -> anyhow::Result<()>;
23-
}
24-
2514
/// Represents the output writer, which can handle different types of outputs.
2615
///
2716
/// This enum provides two variants:
@@ -31,7 +20,11 @@ pub trait IteratorWriter<T> {
3120
/// The variants are selected at runtime based on the configuration provided.
3221
pub enum OutputWriter {
3322
#[allow(private_interfaces)]
34-
Clang(FormattedClangOutputWriter),
23+
Clang(
24+
FormattedClangOutputWriter<
25+
AppendClangOutputWriter<AtomicClangOutputWriter<ClangOutputWriter>>,
26+
>,
27+
),
3528
#[allow(private_interfaces)]
3629
Semantic(SemanticOutputWriter),
3730
}
@@ -43,8 +36,18 @@ impl TryFrom<(&args::BuildSemantic, &config::Output)> for OutputWriter {
4336
let (args, config) = value;
4437
match config {
4538
config::Output::Clang { duplicates, .. } => {
46-
let result = FormattedClangOutputWriter::try_from((args, duplicates))?;
47-
Ok(Self::Clang(result))
39+
let final_file_name = path::Path::new(&args.file_name);
40+
let temp_file_name = final_file_name.with_extension("tmp");
41+
42+
let base_writer =
43+
ClangOutputWriter::try_from((temp_file_name.as_path(), duplicates))?;
44+
let atomic_writer =
45+
AtomicClangOutputWriter::new(base_writer, &temp_file_name, final_file_name);
46+
let append_writer =
47+
AppendClangOutputWriter::new(atomic_writer, args.append, final_file_name);
48+
let formatted_writer = FormattedClangOutputWriter::new(append_writer);
49+
50+
Ok(Self::Clang(formatted_writer))
4851
}
4952
config::Output::Semantic { .. } => {
5053
let path = path::Path::new(&args.file_name);
@@ -61,12 +64,23 @@ impl OutputWriter {
6164
semantics: impl Iterator<Item = semantic::CompilerCall>,
6265
) -> anyhow::Result<()> {
6366
match self {
64-
Self::Clang(writer) => writer.write_array(semantics),
65-
Self::Semantic(writer) => writer.write_array(semantics),
67+
Self::Clang(writer) => writer.write(semantics),
68+
Self::Semantic(writer) => writer.write(semantics),
6669
}
6770
}
6871
}
6972

73+
/// The trait represents a writer for iterator type `T`.
74+
///
75+
/// This trait is implemented by types that can consume an iterator of type `T`
76+
/// and write its elements to some output. The writing process may succeed or fail,
77+
/// returning either `()` on success or an error.
78+
pub trait IteratorWriter<T> {
79+
/// Writes the iterator as a sequence of elements.
80+
/// It consumes the iterator and returns either a nothing or an error.
81+
fn write(self, _: impl Iterator<Item = T>) -> anyhow::Result<()>;
82+
}
83+
7084
/// This writer is used to write the semantic analysis results to a file.
7185
///
7286
/// # Note
@@ -89,44 +103,32 @@ impl TryFrom<&path::Path> for SemanticOutputWriter {
89103
}
90104

91105
impl IteratorWriter<semantic::CompilerCall> for SemanticOutputWriter {
92-
fn write_array(
93-
self,
94-
semantics: impl Iterator<Item = semantic::CompilerCall>,
95-
) -> anyhow::Result<()> {
106+
fn write(self, semantics: impl Iterator<Item = semantic::CompilerCall>) -> anyhow::Result<()> {
96107
JsonSemanticDatabase::write(self.output, semantics)?;
97108

98109
Ok(())
99110
}
100111
}
101112

102113
/// Formats `semantic::CompilerCall` instances into `clang::Entry` objects.
103-
struct FormattedClangOutputWriter {
114+
struct FormattedClangOutputWriter<T: IteratorWriter<clang::Entry>> {
104115
formatter: formatter::EntryFormatter,
105-
writer: AppendClangOutputWriter,
116+
writer: T,
106117
}
107118

108-
impl TryFrom<(&args::BuildSemantic, &config::DuplicateFilter)> for FormattedClangOutputWriter {
109-
type Error = anyhow::Error;
110-
111-
fn try_from(
112-
value: (&args::BuildSemantic, &config::DuplicateFilter),
113-
) -> Result<Self, Self::Error> {
114-
let (args, config) = value;
115-
119+
impl<T: IteratorWriter<clang::Entry>> FormattedClangOutputWriter<T> {
120+
fn new(writer: T) -> Self {
116121
let formatter = formatter::EntryFormatter::new();
117-
let writer = AppendClangOutputWriter::try_from((args, config))?;
118-
119-
Ok(Self { formatter, writer })
122+
Self { formatter, writer }
120123
}
121124
}
122125

123-
impl IteratorWriter<semantic::CompilerCall> for FormattedClangOutputWriter {
124-
fn write_array(
125-
self,
126-
semantics: impl Iterator<Item = semantic::CompilerCall>,
127-
) -> anyhow::Result<()> {
126+
impl<T: IteratorWriter<clang::Entry>> IteratorWriter<semantic::CompilerCall>
127+
for FormattedClangOutputWriter<T>
128+
{
129+
fn write(self, semantics: impl Iterator<Item = semantic::CompilerCall>) -> anyhow::Result<()> {
128130
let entries = semantics.flat_map(|semantic| self.formatter.apply(semantic));
129-
self.writer.write_array(entries)
131+
self.writer.write(entries)
130132
}
131133
}
132134

@@ -136,48 +138,24 @@ impl IteratorWriter<semantic::CompilerCall> for FormattedClangOutputWriter {
136138
/// combining them with new entries, and writing the result back to the file.
137139
/// If the file does not exist and the append option is enabled, it logs a warning
138140
/// and writes only the new entries.
139-
struct AppendClangOutputWriter {
140-
writer: AtomicClangOutputWriter,
141+
struct AppendClangOutputWriter<T: IteratorWriter<clang::Entry>> {
142+
writer: T,
141143
path: Option<path::PathBuf>,
142144
}
143145

144-
impl TryFrom<(&args::BuildSemantic, &config::DuplicateFilter)> for AppendClangOutputWriter {
145-
type Error = anyhow::Error;
146-
147-
fn try_from(
148-
value: (&args::BuildSemantic, &config::DuplicateFilter),
149-
) -> Result<Self, Self::Error> {
150-
let (args, config) = value;
151-
152-
let file_name = path::Path::new(&args.file_name);
146+
impl<T: IteratorWriter<clang::Entry>> AppendClangOutputWriter<T> {
147+
fn new(writer: T, append: bool, file_name: &path::Path) -> Self {
153148
let path = if file_name.exists() {
154149
Some(file_name.to_path_buf())
155150
} else {
156-
if args.append {
151+
if append {
157152
log::warn!("The output file does not exist, the append option is ignored.");
158153
}
159154
None
160155
};
161-
162-
let writer = AtomicClangOutputWriter::try_from((file_name, config))?;
163-
164-
Ok(Self { writer, path })
156+
Self { writer, path }
165157
}
166-
}
167-
168-
impl IteratorWriter<clang::Entry> for AppendClangOutputWriter {
169-
fn write_array(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
170-
if let Some(path) = self.path {
171-
let entries_from_db = Self::read_from_compilation_db(&path)?;
172-
let final_entries = entries_from_db.chain(entries);
173-
self.writer.write_array(final_entries)
174-
} else {
175-
self.writer.write_array(entries)
176-
}
177-
}
178-
}
179158

180-
impl AppendClangOutputWriter {
181159
/// Reads the compilation database from a file.
182160
///
183161
/// NOTE: The function is intentionally not getting any `&self` reference,
@@ -196,39 +174,44 @@ impl AppendClangOutputWriter {
196174
}
197175
}
198176

177+
impl<T: IteratorWriter<clang::Entry>> IteratorWriter<clang::Entry> for AppendClangOutputWriter<T> {
178+
fn write(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
179+
if let Some(path) = self.path {
180+
let entries_from_db = Self::read_from_compilation_db(&path)?;
181+
let final_entries = entries_from_db.chain(entries);
182+
self.writer.write(final_entries)
183+
} else {
184+
self.writer.write(entries)
185+
}
186+
}
187+
}
188+
199189
/// Responsible for writing a JSON compilation database file atomically.
200190
///
201191
/// The file is first written to a temporary file and then renamed to the final file name.
202192
/// This ensures that the output file is not left in an inconsistent state in case of errors.
203-
struct AtomicClangOutputWriter {
204-
writer: ClangOutputWriter,
193+
struct AtomicClangOutputWriter<T: IteratorWriter<clang::Entry>> {
194+
writer: T,
205195
temp_file_name: path::PathBuf,
206196
final_file_name: path::PathBuf,
207197
}
208198

209-
impl TryFrom<(&path::Path, &config::DuplicateFilter)> for AtomicClangOutputWriter {
210-
type Error = anyhow::Error;
211-
212-
fn try_from(value: (&path::Path, &config::DuplicateFilter)) -> Result<Self, Self::Error> {
213-
let (file_name, config) = value;
214-
215-
let temp_file_name = file_name.with_extension("tmp");
216-
let writer = ClangOutputWriter::try_from((temp_file_name.as_path(), config))?;
217-
218-
Ok(Self {
199+
impl<T: IteratorWriter<clang::Entry>> AtomicClangOutputWriter<T> {
200+
fn new(writer: T, temp_file_name: &path::Path, final_file_name: &path::Path) -> Self {
201+
Self {
219202
writer,
220203
temp_file_name: temp_file_name.to_path_buf(),
221-
final_file_name: file_name.to_path_buf(),
222-
})
204+
final_file_name: final_file_name.to_path_buf(),
205+
}
223206
}
224207
}
225208

226-
impl IteratorWriter<clang::Entry> for AtomicClangOutputWriter {
227-
fn write_array(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
209+
impl<T: IteratorWriter<clang::Entry>> IteratorWriter<clang::Entry> for AtomicClangOutputWriter<T> {
210+
fn write(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
228211
let temp_file_name = self.temp_file_name.clone();
229212
let final_file_name = self.final_file_name.clone();
230213

231-
self.writer.write_array(entries)?;
214+
self.writer.write(entries)?;
232215

233216
fs::rename(&temp_file_name, &final_file_name).with_context(|| {
234217
format!(
@@ -268,7 +251,7 @@ impl TryFrom<(&path::Path, &config::DuplicateFilter)> for ClangOutputWriter {
268251
}
269252

270253
impl IteratorWriter<clang::Entry> for ClangOutputWriter {
271-
fn write_array(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
254+
fn write(self, entries: impl Iterator<Item = clang::Entry>) -> anyhow::Result<()> {
272255
let mut filter = self.filter.clone();
273256
let filtered_entries = entries.filter(move |entry| filter.unique(entry));
274257
JsonCompilationDatabase::write(self.output, filtered_entries)?;

0 commit comments

Comments
 (0)