@@ -19,8 +19,8 @@ mod json;
1919
2020use super :: { args, config, intercept, semantic} ;
2121use anyhow:: Context ;
22- use serde :: ser :: SerializeSeq ;
23- use serde :: Serializer ;
22+ use serde_json :: de :: IoRead ;
23+ use serde_json :: StreamDeserializer ;
2424use std:: { fs, io, path} ;
2525use thiserror:: Error ;
2626
@@ -29,8 +29,21 @@ use thiserror::Error;
2929/// The file format in this project is usually a sequence of values. This trait
3030/// provides a type-independent abstraction over the file format.
3131pub trait FileFormat < T > {
32- fn write ( self , _: impl Iterator < Item = T > ) -> Result < ( ) , Error > ;
33- fn read ( self ) -> impl Iterator < Item = Result < T , Error > > ;
32+ fn write ( _: impl io:: Write , _: impl Iterator < Item = T > ) -> Result < ( ) , Error > ;
33+
34+ fn read ( _: impl io:: Read ) -> impl Iterator < Item = Result < T , Error > > ;
35+
36+ /// Reads the entries from the file and ignores any errors.
37+ /// This is not always feasible, when the file format is strict.
38+ fn read_and_ignore ( reader : impl io:: Read , source : path:: PathBuf ) -> impl Iterator < Item = T > {
39+ Self :: read ( reader) . filter_map ( move |result| match result {
40+ Ok ( value) => Some ( value) ,
41+ Err ( error) => {
42+ log:: warn!( "Failed to read entry: {:?} from {:?}" , error, source) ;
43+ None
44+ }
45+ } )
46+ }
3447}
3548
3649#[ derive( Debug , Error ) ]
@@ -48,21 +61,47 @@ pub enum Error {
4861/// The format is a JSON array format, which is a sequence of JSON objects
4962/// enclosed in square brackets. Each object represents a compilation
5063/// command.
51- ///
64+ ///
5265/// # Note
5366/// The format itself is defined in the LLVM project documentation.
5467/// https://clang.llvm.org/docs/JSONCompilationDatabase.html
55- pub trait JsonCompilationDatabase : FileFormat < clang:: Entry > { }
68+ pub struct JsonCompilationDatabase ;
69+
70+ impl FileFormat < clang:: Entry > for JsonCompilationDatabase {
71+ fn write (
72+ writer : impl io:: Write ,
73+ entries : impl Iterator < Item = clang:: Entry > ,
74+ ) -> Result < ( ) , Error > {
75+ json:: write_array ( writer, entries) . map_err ( Error :: Json )
76+ }
77+
78+ fn read ( reader : impl io:: Read ) -> impl Iterator < Item = Result < clang:: Entry , Error > > {
79+ json:: read_array ( reader) . map ( |value| value. map_err ( Error :: Json ) )
80+ }
81+ }
5682
5783/// The trait represents a JSON semantic database format.
58- ///
84+ ///
5985/// The format is a JSON array format, which is a sequence of JSON objects
6086/// enclosed in square brackets. Each object represents a semantic analysis
6187/// result.
6288///
6389/// # Note
6490/// The output format is not stable and may change in future versions.
65- pub trait JsonSemanticDatabase : FileFormat < semantic:: CompilerCall > { }
91+ pub struct JsonSemanticDatabase ;
92+
93+ impl FileFormat < semantic:: CompilerCall > for JsonSemanticDatabase {
94+ fn write (
95+ writer : impl io:: Write ,
96+ entries : impl Iterator < Item = semantic:: CompilerCall > ,
97+ ) -> Result < ( ) , Error > {
98+ json:: write_array ( writer, entries) . map_err ( Error :: Json )
99+ }
100+ fn read ( _: impl io:: Read ) -> impl Iterator < Item = Result < semantic:: CompilerCall , Error > > {
101+ // Not implemented! (No reader for the semantic output in this project.)
102+ std:: iter:: empty ( )
103+ }
104+ }
66105
67106/// The trait represents a database format for execution events.
68107///
@@ -71,7 +110,26 @@ pub trait JsonSemanticDatabase: FileFormat<semantic::CompilerCall> {}
71110///
72111/// # Note
73112/// The output format is not stable and may change in future versions.
74- pub trait ExecutionEventDatabase : FileFormat < intercept:: Event > { }
113+ pub struct ExecutionEventDatabase ;
114+
115+ impl FileFormat < intercept:: Event > for ExecutionEventDatabase {
116+ fn write (
117+ writer : impl io:: Write ,
118+ entries : impl Iterator < Item = intercept:: Event > ,
119+ ) -> Result < ( ) , Error > {
120+ let mut writer = writer;
121+ for entry in entries {
122+ serde_json:: to_writer ( & mut writer, & entry) . map_err ( Error :: Json ) ?;
123+ writer. write_all ( b"\n " ) . map_err ( Error :: IO ) ?;
124+ }
125+ Ok ( ( ) )
126+ }
127+
128+ fn read ( reader : impl io:: Read ) -> impl Iterator < Item = Result < intercept:: Event , Error > > {
129+ let stream = StreamDeserializer :: new ( IoRead :: new ( reader) ) ;
130+ stream. map ( |value| value. map_err ( Error :: Json ) )
131+ }
132+ }
75133
76134/// The trait represents a writer for iterator type `T`.
77135///
@@ -81,16 +139,16 @@ pub trait ExecutionEventDatabase: FileFormat<intercept::Event> {}
81139pub trait IteratorWriter < T > {
82140 /// Writes the iterator as a sequence of elements.
83141 /// It consumes the iterator and returns either a nothing or an error.
84- fn write ( self , _: impl Iterator < Item = T > ) -> anyhow:: Result < ( ) > ;
142+ fn write_array ( self , _: impl Iterator < Item = T > ) -> anyhow:: Result < ( ) > ;
85143}
86144
87145/// Represents the output writer, which can handle different types of outputs.
88146///
89147/// This enum provides two variants:
90- /// - `Clang`: Handles output for Clang compilation databases .
91- /// - `Semantic`: Handles output for semantic analysis results .
148+ /// - `Clang`: Writes output as a JSON compilation database .
149+ /// - `Semantic`: Writes output as a JSON semantic analysis result .
92150///
93- /// The specific behavior of each variant is implemented in their respective types .
151+ /// The variants are selected at runtime based on the configuration provided .
94152pub enum OutputWriter {
95153 #[ allow( private_interfaces) ]
96154 Clang ( FormattedClangOutputWriter ) ,
@@ -117,11 +175,14 @@ impl TryFrom<(&args::BuildSemantic, &config::Output)> for OutputWriter {
117175 }
118176}
119177
120- impl IteratorWriter < semantic:: CompilerCall > for OutputWriter {
121- fn write ( self , semantics : impl Iterator < Item = semantic:: CompilerCall > ) -> anyhow:: Result < ( ) > {
178+ impl OutputWriter {
179+ pub ( crate ) fn write (
180+ self ,
181+ semantics : impl Iterator < Item = semantic:: CompilerCall > ,
182+ ) -> anyhow:: Result < ( ) > {
122183 match self {
123- Self :: Clang ( writer) => writer. write ( semantics) ,
124- Self :: Semantic ( writer) => writer. write ( semantics) ,
184+ Self :: Clang ( writer) => writer. write_array ( semantics) ,
185+ Self :: Semantic ( writer) => writer. write_array ( semantics) ,
125186 }
126187 }
127188}
@@ -148,14 +209,11 @@ impl TryFrom<&path::Path> for SemanticOutputWriter {
148209}
149210
150211impl IteratorWriter < semantic:: CompilerCall > for SemanticOutputWriter {
151- // FIXME: this is the same method as `clang::write` for entries. Should be generalized?
152- fn write ( self , entries : impl Iterator < Item = semantic:: CompilerCall > ) -> anyhow:: Result < ( ) > {
153- let mut ser = serde_json:: Serializer :: pretty ( self . output ) ;
154- let mut seq = ser. serialize_seq ( None ) ?;
155- for entry in entries {
156- seq. serialize_element ( & entry) ?;
157- }
158- seq. end ( ) ?;
212+ fn write_array (
213+ self ,
214+ semantics : impl Iterator < Item = semantic:: CompilerCall > ,
215+ ) -> anyhow:: Result < ( ) > {
216+ JsonSemanticDatabase :: write ( self . output , semantics) ?;
159217
160218 Ok ( ( ) )
161219 }
@@ -183,9 +241,12 @@ impl TryFrom<(&args::BuildSemantic, &config::DuplicateFilter)> for FormattedClan
183241}
184242
185243impl IteratorWriter < semantic:: CompilerCall > for FormattedClangOutputWriter {
186- fn write ( self , semantics : impl Iterator < Item = semantic:: CompilerCall > ) -> anyhow:: Result < ( ) > {
244+ fn write_array (
245+ self ,
246+ semantics : impl Iterator < Item = semantic:: CompilerCall > ,
247+ ) -> anyhow:: Result < ( ) > {
187248 let entries = semantics. flat_map ( |semantic| self . formatter . apply ( semantic) ) ;
188- self . writer . write ( entries)
249+ self . writer . write_array ( entries)
189250 }
190251}
191252
@@ -225,13 +286,13 @@ impl TryFrom<(&args::BuildSemantic, &config::DuplicateFilter)> for AppendClangOu
225286}
226287
227288impl IteratorWriter < clang:: Entry > for AppendClangOutputWriter {
228- fn write ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
289+ fn write_array ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
229290 if let Some ( path) = self . path {
230291 let entries_from_db = Self :: read_from_compilation_db ( & path) ?;
231292 let final_entries = entries_from_db. chain ( entries) ;
232- self . writer . write ( final_entries)
293+ self . writer . write_array ( final_entries)
233294 } else {
234- self . writer . write ( entries)
295+ self . writer . write_array ( entries)
235296 }
236297 }
237298}
@@ -250,13 +311,7 @@ impl AppendClangOutputWriter {
250311 . map ( io:: BufReader :: new)
251312 . with_context ( || format ! ( "Failed to open file: {:?}" , source) ) ?;
252313
253- let entries = clang:: read ( file) . filter_map ( move |candidate| match candidate {
254- Ok ( entry) => Some ( entry) ,
255- Err ( error) => {
256- log:: error!( "Failed to read file: {:?}, reason: {}" , source_copy, error) ;
257- None
258- }
259- } ) ;
314+ let entries = JsonCompilationDatabase :: read_and_ignore ( file, source_copy) ;
260315 Ok ( entries)
261316 }
262317}
@@ -289,11 +344,11 @@ impl TryFrom<(&path::Path, &config::DuplicateFilter)> for AtomicClangOutputWrite
289344}
290345
291346impl IteratorWriter < clang:: Entry > for AtomicClangOutputWriter {
292- fn write ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
347+ fn write_array ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
293348 let temp_file_name = self . temp_file_name . clone ( ) ;
294349 let final_file_name = self . final_file_name . clone ( ) ;
295350
296- self . writer . write ( entries) ?;
351+ self . writer . write_array ( entries) ?;
297352
298353 fs:: rename ( & temp_file_name, & final_file_name) . with_context ( || {
299354 format ! (
@@ -333,10 +388,10 @@ impl TryFrom<(&path::Path, &config::DuplicateFilter)> for ClangOutputWriter {
333388}
334389
335390impl IteratorWriter < clang:: Entry > for ClangOutputWriter {
336- fn write ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
391+ fn write_array ( self , entries : impl Iterator < Item = clang:: Entry > ) -> anyhow:: Result < ( ) > {
337392 let mut filter = self . filter . clone ( ) ;
338393 let filtered_entries = entries. filter ( move |entry| filter. unique ( entry) ) ;
339- clang :: write ( self . output , filtered_entries) ?;
394+ JsonCompilationDatabase :: write ( self . output , filtered_entries) ?;
340395 Ok ( ( ) )
341396 }
342397}
0 commit comments