66//! It can also alter the compiler flags of the compiler calls. The actions
77//! are defined in the configuration this module is given.
88
9+ use crate :: config:: PathFormat ;
910use crate :: { config, semantic} ;
10- use std:: { io , path } ;
11+ use std:: io ;
1112use thiserror:: Error ;
1213
1314/// Responsible to transform the semantic of an executed command.
1415pub trait Transformation : Send {
15- fn apply ( & self , _: semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > ;
16- }
17-
18- #[ derive( Debug , Error ) ]
19- pub enum Error {
20- // FIXME: Should we report the path that failed?
21- #[ error( "Path canonicalize failed: {0}" ) ]
22- PathCanonicalize ( #[ from] io:: Error ) ,
23- #[ error( "Path {0} can't be relative to {1}" ) ]
24- PathsCannotBeRelative ( path:: PathBuf , path:: PathBuf ) ,
25- #[ error( "Configuration instructed to filter out" ) ]
26- FilteredOut ,
16+ fn apply ( & self , _: semantic:: CompilerCall ) -> semantic:: Recognition < semantic:: CompilerCall > ;
2717}
2818
2919/// FilterAndFormat is a transformation that filters and formats the compiler calls.
3020pub struct FilterAndFormat {
31- filter : filter_by_compiler:: FilterByCompiler ,
32- formatter : formatter:: PathFormatter ,
21+ format_canonical : formatter:: PathFormatter ,
22+ filter_by_compiler : filter_by_compiler:: FilterByCompiler ,
23+ filter_by_source : filter_by_source_dir:: FilterBySourceDir ,
24+ format_by_config : formatter:: PathFormatter ,
3325}
3426
3527impl Transformation for FilterAndFormat {
36- fn apply ( & self , input : semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > {
37- let candidate = self . filter . apply ( input) ?;
38- let formatted = self . formatter . apply ( candidate) ?;
39- Ok ( formatted)
28+ fn apply (
29+ & self ,
30+ input : semantic:: CompilerCall ,
31+ ) -> semantic:: Recognition < semantic:: CompilerCall > {
32+ // FIXME: this is ugly, but could not find a better way to do it.
33+ // The methods are returning different errors in `Result`.
34+ // While this method returns a `Recognition` enum.
35+ match self . format_canonical . apply ( input) {
36+ Ok ( candidate) => match self . filter_by_compiler . apply ( candidate) {
37+ Ok ( candidate) => match self . filter_by_source . apply ( candidate) {
38+ Ok ( candidate) => match self . format_by_config . apply ( candidate) {
39+ Ok ( candidate) => semantic:: Recognition :: Success ( candidate) ,
40+ Err ( error) => semantic:: Recognition :: Error ( error. to_string ( ) ) ,
41+ } ,
42+ Err ( error) => semantic:: Recognition :: Ignored ( error. to_string ( ) ) ,
43+ } ,
44+ Err ( error) => semantic:: Recognition :: Ignored ( error. to_string ( ) ) ,
45+ } ,
46+ Err ( error) => semantic:: Recognition :: Error ( error. to_string ( ) ) ,
47+ }
4048 }
4149}
4250
4351#[ derive( Debug , Error ) ]
4452pub enum FilterAndFormatError {
45- #[ error( "Semantic filter configuration error: {0}" ) ]
46- FilterByCompiler ( #[ from] filter_by_compiler:: ConfigurationError ) ,
4753 #[ error( "Path formatter configuration error: {0}" ) ]
4854 PathFormatter ( #[ from] formatter:: ConfigurationError ) ,
55+ #[ error( "Compiler filter configuration error: {0}" ) ]
56+ FilterByCompiler ( #[ from] filter_by_compiler:: ConfigurationError ) ,
57+ #[ error( "Source filter configuration error: {0}" ) ]
58+ FilterBySourceDir ( #[ from] filter_by_source_dir:: ConfigurationError ) ,
4959}
5060
5161impl TryFrom < & config:: Output > for FilterAndFormat {
@@ -59,24 +69,42 @@ impl TryFrom<&config::Output> for FilterAndFormat {
5969 sources,
6070 ..
6171 } => {
62- let filter = compilers. as_slice ( ) . try_into ( ) ?;
63- let formatter = if sources. only_existing_files {
64- ( & format. paths ) . try_into ( ) ?
72+ if !sources. only_existing_files {
73+ log:: warn!( "Access to the filesystem is disabled in source filters." ) ;
74+ }
75+ let format_canonical = if sources. only_existing_files {
76+ let canonical_config = PathFormat :: default ( ) ;
77+ formatter:: PathFormatter :: try_from ( & canonical_config) ?
78+ } else {
79+ formatter:: PathFormatter :: default ( )
80+ } ;
81+ let filter_by_compiler = compilers. as_slice ( ) . try_into ( ) ?;
82+ let filter_by_source = sources. try_into ( ) ?;
83+ let format_by_config = if sources. only_existing_files {
84+ formatter:: PathFormatter :: try_from ( & format. paths ) ?
6585 } else {
66- log:: warn!(
67- "The output formatting configuration is ignored. \
68- Access to the filesystem is disabled in source filters."
69- ) ;
7086 formatter:: PathFormatter :: default ( )
7187 } ;
7288
73- Ok ( FilterAndFormat { filter, formatter } )
89+ Ok ( FilterAndFormat {
90+ format_canonical,
91+ filter_by_compiler,
92+ filter_by_source,
93+ format_by_config,
94+ } )
7495 }
7596 config:: Output :: Semantic { .. } => {
76- // This will do no filtering and no formatting.
77- let filter = filter_by_compiler:: FilterByCompiler :: default ( ) ;
78- let formatter = formatter:: PathFormatter :: default ( ) ;
79- Ok ( FilterAndFormat { filter, formatter } )
97+ let format_canonical = formatter:: PathFormatter :: default ( ) ;
98+ let filter_by_compiler = filter_by_compiler:: FilterByCompiler :: default ( ) ;
99+ let filter_by_source = filter_by_source_dir:: FilterBySourceDir :: default ( ) ;
100+ let format_by_config = formatter:: PathFormatter :: default ( ) ;
101+
102+ Ok ( FilterAndFormat {
103+ format_canonical,
104+ filter_by_compiler,
105+ filter_by_source,
106+ format_by_config,
107+ } )
80108 }
81109 }
82110 }
@@ -95,17 +123,25 @@ mod formatter {
95123
96124 use super :: * ;
97125 use std:: env;
98- use std:: path:: { Path , PathBuf } ;
126+ use std:: path;
99127
100128 #[ derive( Default , Debug ) ]
101129 pub enum PathFormatter {
102- DoFormat ( config:: PathFormat , PathBuf ) ,
130+ DoFormat ( config:: PathFormat , path :: PathBuf ) ,
103131 #[ default]
104132 SkipFormat ,
105133 }
134+ #[ derive( Debug , Error ) ]
135+ pub enum Error {
136+ // FIXME: Should we report the path that failed?
137+ #[ error( "Path canonicalize failed: {0}" ) ]
138+ PathCanonicalize ( #[ from] io:: Error ) ,
139+ #[ error( "Path {0} can't be relative to {1}" ) ]
140+ PathsCannotBeRelative ( path:: PathBuf , path:: PathBuf ) ,
141+ }
106142
107- impl Transformation for PathFormatter {
108- fn apply ( & self , call : semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > {
143+ impl PathFormatter {
144+ pub fn apply ( & self , call : semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > {
109145 match self {
110146 PathFormatter :: SkipFormat => Ok ( call) ,
111147 PathFormatter :: DoFormat ( config, cwd) => call. format ( config, cwd) ,
@@ -138,7 +174,7 @@ mod formatter {
138174 }
139175
140176 /// Compute the absolute path from the root directory if the path is relative.
141- fn absolute_to ( root : & Path , path : & Path ) -> Result < PathBuf , Error > {
177+ fn absolute_to ( root : & path :: Path , path : & path :: Path ) -> Result < path :: PathBuf , Error > {
142178 if path. is_absolute ( ) {
143179 Ok ( path. canonicalize ( ) ?)
144180 } else {
@@ -147,7 +183,7 @@ mod formatter {
147183 }
148184
149185 /// Compute the relative path from the root directory.
150- fn relative_to ( root : & Path , path : & Path ) -> Result < PathBuf , Error > {
186+ fn relative_to ( root : & path :: Path , path : & path :: Path ) -> Result < path :: PathBuf , Error > {
151187 // This is a naive implementation that assumes the root is
152188 // on the same filesystem/volume as the path.
153189 let mut root_components = root. components ( ) ;
@@ -184,7 +220,7 @@ mod formatter {
184220 }
185221
186222 // Count remaining components in the root to determine how many `..` are needed
187- let mut result = PathBuf :: new ( ) ;
223+ let mut result = path :: PathBuf :: new ( ) ;
188224 for _ in remaining_root_components {
189225 result. push ( path:: Component :: ParentDir ) ;
190226 }
@@ -213,7 +249,7 @@ mod formatter {
213249
214250 /// Convenient function to resolve the path based on the configuration.
215251 impl config:: PathResolver {
216- fn resolve ( & self , base : & Path , path : & Path ) -> Result < PathBuf , Error > {
252+ fn resolve ( & self , base : & path :: Path , path : & path :: Path ) -> Result < path :: PathBuf , Error > {
217253 match self {
218254 config:: PathResolver :: Canonical => {
219255 let result = path. canonicalize ( ) ?;
@@ -227,7 +263,7 @@ mod formatter {
227263 }
228264
229265 impl semantic:: CompilerCall {
230- pub fn format ( self , config : & config:: PathFormat , cwd : & Path ) -> Result < Self , Error > {
266+ pub fn format ( self , config : & config:: PathFormat , cwd : & path :: Path ) -> Result < Self , Error > {
231267 // The working directory is usually an absolute path.
232268 let working_dir = self . working_dir . canonicalize ( ) ?;
233269
@@ -247,7 +283,7 @@ mod formatter {
247283 pub fn format (
248284 self ,
249285 config : & config:: PathFormat ,
250- working_dir : & Path ,
286+ working_dir : & path :: Path ,
251287 ) -> Result < Self , Error > {
252288 match self {
253289 semantic:: CompilerPass :: Compile {
@@ -256,7 +292,7 @@ mod formatter {
256292 flags,
257293 } => {
258294 let source = config. file . resolve ( working_dir, & source) ?;
259- let output: Option < PathBuf > = output
295+ let output: Option < path :: PathBuf > = output
260296 . map ( |candidate| config. output . resolve ( working_dir, & candidate) )
261297 . transpose ( ) ?;
262298 Ok ( semantic:: CompilerPass :: Compile {
@@ -517,29 +553,36 @@ mod filter_by_compiler {
517553 /// Transformation contains rearranged information from the configuration.
518554 ///
519555 /// The configuration is a list of instructions on how to transform the compiler call.
520- /// The transformation groups the instructions by the compiler path, so it can be
521- /// applied to the compiler call when it matches the path.
556+ /// The transformations are grouped by the compiler path, so it can be applied to the
557+ /// compiler call when it matches the path.
522558 #[ derive( Default , Debug ) ]
523559 pub struct FilterByCompiler {
524560 compilers : HashMap < path:: PathBuf , Vec < config:: Compiler > > ,
525561 }
526562
527- impl Transformation for FilterByCompiler {
528- fn apply ( & self , input : semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > {
563+ #[ derive( Debug , Error ) ]
564+ pub enum Error {
565+ #[ error( "Configuration instructed to filter out" ) ]
566+ FilteredOut ,
567+ }
568+
569+ impl FilterByCompiler {
570+ pub fn apply (
571+ & self ,
572+ input : semantic:: CompilerCall ,
573+ ) -> Result < semantic:: CompilerCall , Error > {
529574 if let Some ( configs) = self . compilers . get ( & input. compiler ) {
530575 Self :: apply_when_match_compiler ( configs. as_slice ( ) , input)
531576 } else {
532577 Ok ( input)
533578 }
534579 }
535- }
536580
537- impl FilterByCompiler {
538581 /// Apply the transformation to the compiler call.
539582 ///
540583 /// Multiple configurations can be applied to the same compiler call.
541584 /// And depending on the instruction from the configuration, the compiler call
542- /// can be ignored, modified, or left unchanged. The conditional ignore will
585+ /// can be ignored, modified, or left unchanged. The conditional ignoring will
543586 /// check if the compiler call matches the flags defined in the configuration.
544587 fn apply_when_match_compiler (
545588 configs : & [ config:: Compiler ] ,
@@ -730,7 +773,6 @@ mod filter_by_compiler {
730773 mod tests {
731774 use super :: * ;
732775 use crate :: config:: { Arguments , Compiler , IgnoreOrConsider } ;
733- use crate :: semantic:: transformation:: Transformation ;
734776 use crate :: semantic:: { CompilerCall , CompilerPass } ;
735777 use std:: path:: PathBuf ;
736778
@@ -998,11 +1040,20 @@ mod filter_by_source_dir {
9981040 filters : Vec < config:: DirectoryFilter > ,
9991041 }
10001042
1001- impl Transformation for FilterBySourceDir {
1043+ #[ derive( Debug , Error ) ]
1044+ pub enum Error {
1045+ #[ error( "Configuration instructed to filter out" ) ]
1046+ FilteredOut ,
1047+ }
1048+
1049+ impl FilterBySourceDir {
10021050 // FIXME: This is currently ignore the whole compiler call if any of the
10031051 // pass matches the filter. This should be changed to ignore only the
10041052 // pass that matches the filter.
1005- fn apply ( & self , input : semantic:: CompilerCall ) -> Result < semantic:: CompilerCall , Error > {
1053+ pub fn apply (
1054+ & self ,
1055+ input : semantic:: CompilerCall ,
1056+ ) -> Result < semantic:: CompilerCall , Error > {
10061057 // Check if the compiler call matches the source directory filter
10071058 for filter in & self . filters {
10081059 // Check the source for each pass
@@ -1099,10 +1150,8 @@ mod filter_by_source_dir {
10991150
11001151 #[ cfg( test) ]
11011152 mod tests {
1102- use super :: super :: Error ;
1103- use super :: { ConfigurationError , FilterBySourceDir } ;
1153+ use super :: { ConfigurationError , Error , FilterBySourceDir } ;
11041154 use crate :: config:: { DirectoryFilter , Ignore , SourceFilter } ;
1105- use crate :: semantic:: transformation:: Transformation ;
11061155 use crate :: semantic:: { CompilerCall , CompilerPass } ;
11071156 use std:: path:: PathBuf ;
11081157
0 commit comments