@@ -11,17 +11,6 @@ use anyhow::Context;
11
11
use formats:: { FileFormat , JsonCompilationDatabase , JsonSemanticDatabase } ;
12
12
use std:: { fs, io, path} ;
13
13
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
-
25
14
/// Represents the output writer, which can handle different types of outputs.
26
15
///
27
16
/// This enum provides two variants:
@@ -31,7 +20,11 @@ pub trait IteratorWriter<T> {
31
20
/// The variants are selected at runtime based on the configuration provided.
32
21
pub enum OutputWriter {
33
22
#[ allow( private_interfaces) ]
34
- Clang ( FormattedClangOutputWriter ) ,
23
+ Clang (
24
+ FormattedClangOutputWriter <
25
+ AppendClangOutputWriter < AtomicClangOutputWriter < ClangOutputWriter > > ,
26
+ > ,
27
+ ) ,
35
28
#[ allow( private_interfaces) ]
36
29
Semantic ( SemanticOutputWriter ) ,
37
30
}
@@ -43,8 +36,18 @@ impl TryFrom<(&args::BuildSemantic, &config::Output)> for OutputWriter {
43
36
let ( args, config) = value;
44
37
match config {
45
38
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) )
48
51
}
49
52
config:: Output :: Semantic { .. } => {
50
53
let path = path:: Path :: new ( & args. file_name ) ;
@@ -61,12 +64,23 @@ impl OutputWriter {
61
64
semantics : impl Iterator < Item = semantic:: CompilerCall > ,
62
65
) -> anyhow:: Result < ( ) > {
63
66
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) ,
66
69
}
67
70
}
68
71
}
69
72
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
+
70
84
/// This writer is used to write the semantic analysis results to a file.
71
85
///
72
86
/// # Note
@@ -89,44 +103,32 @@ impl TryFrom<&path::Path> for SemanticOutputWriter {
89
103
}
90
104
91
105
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 < ( ) > {
96
107
JsonSemanticDatabase :: write ( self . output , semantics) ?;
97
108
98
109
Ok ( ( ) )
99
110
}
100
111
}
101
112
102
113
/// Formats `semantic::CompilerCall` instances into `clang::Entry` objects.
103
- struct FormattedClangOutputWriter {
114
+ struct FormattedClangOutputWriter < T : IteratorWriter < clang :: Entry > > {
104
115
formatter : formatter:: EntryFormatter ,
105
- writer : AppendClangOutputWriter ,
116
+ writer : T ,
106
117
}
107
118
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 {
116
121
let formatter = formatter:: EntryFormatter :: new ( ) ;
117
- let writer = AppendClangOutputWriter :: try_from ( ( args, config) ) ?;
118
-
119
- Ok ( Self { formatter, writer } )
122
+ Self { formatter, writer }
120
123
}
121
124
}
122
125
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 < ( ) > {
128
130
let entries = semantics. flat_map ( |semantic| self . formatter . apply ( semantic) ) ;
129
- self . writer . write_array ( entries)
131
+ self . writer . write ( entries)
130
132
}
131
133
}
132
134
@@ -136,48 +138,24 @@ impl IteratorWriter<semantic::CompilerCall> for FormattedClangOutputWriter {
136
138
/// combining them with new entries, and writing the result back to the file.
137
139
/// If the file does not exist and the append option is enabled, it logs a warning
138
140
/// and writes only the new entries.
139
- struct AppendClangOutputWriter {
140
- writer : AtomicClangOutputWriter ,
141
+ struct AppendClangOutputWriter < T : IteratorWriter < clang :: Entry > > {
142
+ writer : T ,
141
143
path : Option < path:: PathBuf > ,
142
144
}
143
145
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 {
153
148
let path = if file_name. exists ( ) {
154
149
Some ( file_name. to_path_buf ( ) )
155
150
} else {
156
- if args . append {
151
+ if append {
157
152
log:: warn!( "The output file does not exist, the append option is ignored." ) ;
158
153
}
159
154
None
160
155
} ;
161
-
162
- let writer = AtomicClangOutputWriter :: try_from ( ( file_name, config) ) ?;
163
-
164
- Ok ( Self { writer, path } )
156
+ Self { writer, path }
165
157
}
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
- }
179
158
180
- impl AppendClangOutputWriter {
181
159
/// Reads the compilation database from a file.
182
160
///
183
161
/// NOTE: The function is intentionally not getting any `&self` reference,
@@ -196,39 +174,44 @@ impl AppendClangOutputWriter {
196
174
}
197
175
}
198
176
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
+
199
189
/// Responsible for writing a JSON compilation database file atomically.
200
190
///
201
191
/// The file is first written to a temporary file and then renamed to the final file name.
202
192
/// 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 ,
205
195
temp_file_name : path:: PathBuf ,
206
196
final_file_name : path:: PathBuf ,
207
197
}
208
198
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 {
219
202
writer,
220
203
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
+ }
223
206
}
224
207
}
225
208
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 < ( ) > {
228
211
let temp_file_name = self . temp_file_name . clone ( ) ;
229
212
let final_file_name = self . final_file_name . clone ( ) ;
230
213
231
- self . writer . write_array ( entries) ?;
214
+ self . writer . write ( entries) ?;
232
215
233
216
fs:: rename ( & temp_file_name, & final_file_name) . with_context ( || {
234
217
format ! (
@@ -268,7 +251,7 @@ impl TryFrom<(&path::Path, &config::DuplicateFilter)> for ClangOutputWriter {
268
251
}
269
252
270
253
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 < ( ) > {
272
255
let mut filter = self . filter . clone ( ) ;
273
256
let filtered_entries = entries. filter ( move |entry| filter. unique ( entry) ) ;
274
257
JsonCompilationDatabase :: write ( self . output , filtered_entries) ?;
0 commit comments