Skip to content

Commit 49c4ae9

Browse files
committed
rust: transformation calls all filtering
1 parent 12cae4d commit 49c4ae9

File tree

1 file changed

+106
-57
lines changed

1 file changed

+106
-57
lines changed

rust/bear/src/semantic/transformation.rs

Lines changed: 106 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,56 @@
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;
910
use crate::{config, semantic};
10-
use std::{io, path};
11+
use std::io;
1112
use thiserror::Error;
1213

1314
/// Responsible to transform the semantic of an executed command.
1415
pub 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.
3020
pub 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

3527
impl 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)]
4452
pub 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

5161
impl 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

Comments
 (0)