Skip to content

Commit b4adb08

Browse files
committed
refactor(config): simplify configuration loading by removing manual setup
Signed-off-by: azjezz <[email protected]>
1 parent 0d1bee8 commit b4adb08

File tree

7 files changed

+54
-161
lines changed

7 files changed

+54
-161
lines changed

src/commands/init.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use crate::error::Error;
2525
use crate::utils::version::extract_minimum_php_version;
2626

2727
const CONFIGURATION_TEMPLATE: &str = r#"# Welcome to Mago!
28-
# For full documentation, see https://mago.carthage.software/
28+
# For full documentation, see https://mago.carthage.software/tools/overview
2929
php_version = "{php_version}"
3030
3131
[source]

src/config/analyzer.rs

Lines changed: 2 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
use config::ConfigBuilder;
2-
use config::Value;
3-
use config::ValueKind;
4-
use config::builder::BuilderState;
5-
use mago_analyzer::settings::Settings;
6-
use mago_php_version::PHPVersion;
71
use serde::Deserialize;
82
use serde::Serialize;
93

10-
use crate::config::ConfigurationEntry;
11-
use crate::error::Error;
4+
use mago_analyzer::settings::Settings;
5+
use mago_php_version::PHPVersion;
126

137
/// Configuration options for the static analyzer.
148
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -134,49 +128,6 @@ impl AnalyzerConfiguration {
134128
}
135129
}
136130

137-
impl ConfigurationEntry for AnalyzerConfiguration {
138-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error> {
139-
let defaults = Self::default();
140-
141-
builder
142-
.set_default(
143-
"analyze.excludes",
144-
Value::new(None, ValueKind::Array(self.excludes.into_iter().map(Value::from).collect::<Vec<_>>())),
145-
)?
146-
.set_default(
147-
"analyze.ignore",
148-
Value::new(None, ValueKind::Array(self.ignore.into_iter().map(Value::from).collect::<Vec<_>>())),
149-
)?
150-
.set_default("analyze.mixed-issues", defaults.mixed_issues)?
151-
.set_default("analyze.falsable-issues", defaults.falsable_issues)?
152-
.set_default("analyze.nullable-issues", defaults.nullable_issues)?
153-
.set_default("analyze.redundancy-issues", defaults.redundancy_issues)?
154-
.set_default("analyze.reference-issues", defaults.reference_issues)?
155-
.set_default("analyze.unreachable-issues", defaults.unreachable_issues)?
156-
.set_default("analyze.deprecation-issues", defaults.deprecation_issues)?
157-
.set_default("analyze.impossibility-issues", defaults.impossibility_issues)?
158-
.set_default("analyze.ambiguity-issues", defaults.ambiguity_issues)?
159-
.set_default("analyze.existence-issues", defaults.existence_issues)?
160-
.set_default("analyze.template-issues", defaults.template_issues)?
161-
.set_default("analyze.argument-issues", defaults.argument_issues)?
162-
.set_default("analyze.operand-issues", defaults.operand_issues)?
163-
.set_default("analyze.property-issues", defaults.property_issues)?
164-
.set_default("analyze.generator-issues", defaults.generator_issues)?
165-
.set_default("analyze.array-issues", defaults.array_issues)?
166-
.set_default("analyze.return-issues", defaults.return_issues)?
167-
.set_default("analyze.method-issues", defaults.method_issues)?
168-
.set_default("analyze.iterator-issues", defaults.iterator_issues)?
169-
.set_default("analyze.find-unused-definitions", defaults.find_unused_definitions)?
170-
.set_default("analyze.find-unused-expressions", defaults.find_unused_expressions)?
171-
.set_default("analyze.analyze-dead-code", defaults.analyze_dead_code)?
172-
.set_default("analyze.memoize-properties", defaults.memoize_properties)?
173-
.set_default("analyze.allow-possibly-undefined-array-keys", defaults.allow_possibly_undefined_array_keys)?
174-
.set_default("analyze.check-throws", defaults.check_throws)?
175-
.set_default("analyze.perform-heuristic-checks", defaults.perform_heuristic_checks)
176-
.map_err(Error::from)
177-
}
178-
}
179-
180131
impl Default for AnalyzerConfiguration {
181132
fn default() -> Self {
182133
let defaults = Settings::default();

src/config/formatter.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,8 @@
1-
use config::ConfigBuilder;
2-
use config::Value;
3-
use config::ValueKind;
4-
use config::builder::BuilderState;
51
use serde::Deserialize;
62
use serde::Serialize;
73

84
use mago_formatter::settings::*;
95

10-
use crate::config::ConfigurationEntry;
11-
use crate::error::Error;
12-
136
/// Configuration options for formatting source code.
147
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
158
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
@@ -22,9 +15,3 @@ pub struct FormatterConfiguration {
2215
#[serde(flatten)]
2316
pub settings: FormatSettings,
2417
}
25-
26-
impl ConfigurationEntry for FormatterConfiguration {
27-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error> {
28-
builder.set_default("format.excludes", Value::new(None, ValueKind::Array(vec![]))).map_err(Error::from)
29-
}
30-
}

src/config/linter.rs

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
1-
use config::ConfigBuilder;
2-
use config::builder::BuilderState;
3-
use mago_linter::integration::Integration;
41
use serde::Deserialize;
52
use serde::Serialize;
63

4+
use mago_linter::integration::Integration;
75
use mago_linter::settings::RulesSettings;
86

9-
use crate::config::ConfigurationEntry;
10-
use crate::error::Error;
11-
127
#[derive(Default, Debug, Clone, Serialize, Deserialize)]
13-
#[serde(rename_all = "kebab-case", deny_unknown_fields)]
8+
#[serde(default, rename_all = "kebab-case", deny_unknown_fields)]
149
pub struct LinterConfiguration {
1510
/// A list of patterns to exclude from linting.
1611
pub excludes: Vec<String>,
@@ -21,17 +16,3 @@ pub struct LinterConfiguration {
2116
/// Settings for various linting rules.
2217
pub rules: RulesSettings,
2318
}
24-
25-
impl ConfigurationEntry for LinterConfiguration {
26-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error> {
27-
use ::config::Value;
28-
use ::config::ValueKind;
29-
30-
let builder = builder
31-
.set_default("linter.excludes", Value::new(None, ValueKind::Array(vec![])))?
32-
.set_default("linter.integrations", Value::new(None, ValueKind::Array(vec![])))?
33-
.set_default("linter.rules", Value::new(None, ValueKind::Table(Default::default())))?;
34-
35-
Ok(builder)
36-
}
37-
}

src/config/mod.rs

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,18 @@ use std::env::home_dir;
22
use std::path::Path;
33
use std::path::PathBuf;
44

5-
use analyzer::AnalyzerConfiguration;
65
use config::Config;
7-
use config::ConfigBuilder;
86
use config::Environment;
97
use config::File;
108
use config::FileFormat;
119
use config::Value;
1210
use config::ValueKind;
13-
use config::builder::BuilderState;
1411
use serde::Deserialize;
12+
use serde::Serialize;
1513

1614
use mago_php_version::PHPVersion;
17-
use serde::Serialize;
1815

16+
use crate::config::analyzer::AnalyzerConfiguration;
1917
use crate::config::formatter::FormatterConfiguration;
2018
use crate::config::linter::LinterConfiguration;
2119
use crate::config::source::SourceConfiguration;
@@ -48,17 +46,14 @@ pub struct Configuration {
4846

4947
/// Configuration options for the linter.
5048
#[serde(default)]
51-
#[serde(alias = "lint")]
5249
pub linter: LinterConfiguration,
5350

5451
/// Configuration options for the formatter.
5552
#[serde(default)]
56-
#[serde(alias = "format")]
5753
pub formatter: FormatterConfiguration,
5854

5955
/// Configuration options for the analyzer.
6056
#[serde(default)]
61-
#[serde(alias = "analyser", alias = "analyze", alias = "analyse")]
6257
pub analyzer: AnalyzerConfiguration,
6358

6459
/// The log filter.
@@ -68,7 +63,8 @@ pub struct Configuration {
6863
///
6964
/// If this field is to be removed, serde will complain about an unknown field in the configuration
7065
/// when `MAGO_LOG` is set due to the `deny_unknown_fields` attribute and the use of `Environment` source.
71-
#[serde(skip_serializing)]
66+
#[serde(default, skip_serializing)]
67+
#[allow(dead_code)]
7268
log: Value,
7369
}
7470

@@ -106,29 +102,38 @@ impl Configuration {
106102
allow_unsupported_php_version: bool,
107103
) -> Result<Configuration, Error> {
108104
let workspace_dir = workspace.clone().unwrap_or_else(|| CURRENT_DIR.to_path_buf());
105+
let workspace_config_path = workspace_dir.join(CONFIGURATION_FILE);
106+
107+
let mut configuration = Configuration::from_workspace(workspace_dir);
108+
let mut builder = Config::builder().add_source(Config::try_from(&configuration)?);
109109

110-
let mut builder = Config::builder();
111110
if let Some(file) = file {
111+
tracing::debug!("Sourcing configuration from {}.", file.display());
112+
112113
builder = builder.add_source(File::from(file).required(true).format(FileFormat::Toml));
113114
} else {
114-
let global_config_roots =
115-
[std::env::var_os("XDG_CONFIG_HOME").map(PathBuf::from), home_dir()].into_iter().flatten();
116-
115+
let global_config_roots = [std::env::var_os("XDG_CONFIG_HOME").map(PathBuf::from), home_dir()];
117116
for global_config_root in global_config_roots {
118-
builder = builder.add_source(
119-
File::from(global_config_root.join(CONFIGURATION_FILE)).required(false).format(FileFormat::Toml),
120-
);
121-
}
117+
let Some(global_config_root) = global_config_root else {
118+
continue;
119+
};
122120

123-
builder = builder
124-
.add_source(File::from(workspace_dir.join(CONFIGURATION_FILE)).required(false).format(FileFormat::Toml))
125-
}
121+
let global_config_path = global_config_root.join(CONFIGURATION_FILE);
126122

127-
builder = builder.add_source(Environment::with_prefix(ENVIRONMENT_PREFIX));
123+
tracing::debug!("Sourcing global configuration from {}.", global_config_path.display());
128124

129-
let mut configuration = Configuration::from_workspace(workspace_dir);
125+
builder = builder.add_source(File::from(global_config_path).required(false).format(FileFormat::Toml));
126+
}
130127

131-
configuration = configuration.configure(builder)?.build()?.try_deserialize::<Configuration>()?;
128+
tracing::debug!("Sourcing workspace configuration from {}.", workspace_config_path.display());
129+
130+
builder = builder.add_source(File::from(workspace_config_path).required(false).format(FileFormat::Toml));
131+
}
132+
133+
configuration = builder
134+
.add_source(Environment::with_prefix(ENVIRONMENT_PREFIX))
135+
.build()?
136+
.try_deserialize::<Configuration>()?;
132137

133138
if allow_unsupported_php_version && !configuration.allow_unsupported_php_version {
134139
tracing::warn!("Allowing unsupported PHP versions.");
@@ -183,32 +188,7 @@ impl Configuration {
183188
}
184189
}
185190

186-
trait ConfigurationEntry {
187-
/// Configures the builder with the entry.
188-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error>;
189-
190-
fn normalize(&mut self) -> Result<(), Error> {
191-
Ok(())
192-
}
193-
}
194-
195-
impl ConfigurationEntry for Configuration {
196-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error> {
197-
let mut builder = builder
198-
.set_default("threads", Value::new(None, ValueKind::U64(self.threads as u64)))?
199-
.set_default("stack-size", Value::new(None, ValueKind::U64(self.stack_size as u64)))?
200-
.set_default("php-version", Value::new(None, ValueKind::String(self.php_version.to_string())))?
201-
.set_default("allow-unsupported-php-version", self.allow_unsupported_php_version)?
202-
.set_default("log", self.log)?;
203-
204-
builder = self.source.configure(builder)?;
205-
builder = self.linter.configure(builder)?;
206-
builder = self.formatter.configure(builder)?;
207-
builder = self.analyzer.configure(builder)?;
208-
209-
Ok(builder)
210-
}
211-
191+
impl Configuration {
212192
fn normalize(&mut self) -> Result<(), Error> {
213193
match self.threads {
214194
0 => {
@@ -252,9 +232,6 @@ impl ConfigurationEntry for Configuration {
252232
}
253233

254234
self.source.normalize()?;
255-
self.linter.normalize()?;
256-
self.formatter.normalize()?;
257-
self.analyzer.normalize()?;
258235

259236
Ok(())
260237
}

src/config/source.rs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
11
use std::path::PathBuf;
22

3-
use config::ConfigBuilder;
4-
use config::Value;
5-
use config::ValueKind;
6-
use config::builder::BuilderState;
73
use serde::Deserialize;
84
use serde::Serialize;
95

106
use crate::config::CURRENT_DIR;
11-
use crate::config::ConfigurationEntry;
7+
use crate::consts::PHP_EXTENSION;
128
use crate::error::Error;
139

1410
/// Configuration options for source discovery.
@@ -25,21 +21,25 @@ pub struct SourceConfiguration {
2521
/// If empty, all files in the workspace directory are included.
2622
///
2723
/// Defaults to `[]`.
24+
#[serde(default)]
2825
pub paths: Vec<PathBuf>,
2926

3027
/// Paths to non-user defined files to include in the scan.
3128
///
3229
/// Defaults to `[]`.
30+
#[serde(default)]
3331
pub includes: Vec<PathBuf>,
3432

3533
/// Patterns to exclude from the scan.
3634
///
3735
/// Defaults to `[]`.
36+
#[serde(default)]
3837
pub excludes: Vec<String>,
3938

4039
/// File extensions to filter by.
4140
///
4241
/// Defaults to `[".php"]`.
42+
#[serde(default = "default_extensions")]
4343
pub extensions: Vec<String>,
4444
}
4545

@@ -54,28 +54,18 @@ impl SourceConfiguration {
5454
///
5555
/// A new `SourceConfiguration` with the given workspace directory.
5656
pub fn from_workspace(workspace: PathBuf) -> Self {
57-
Self { workspace, paths: vec![], includes: vec![], excludes: vec![], extensions: vec![] }
57+
Self {
58+
workspace,
59+
paths: vec![],
60+
includes: vec![],
61+
excludes: vec![],
62+
extensions: vec![PHP_EXTENSION.to_string()],
63+
}
5864
}
5965
}
6066

61-
impl ConfigurationEntry for SourceConfiguration {
62-
fn configure<St: BuilderState>(self, builder: ConfigBuilder<St>) -> Result<ConfigBuilder<St>, Error> {
63-
builder
64-
.set_default(
65-
"source.workspace",
66-
Value::new(None, ValueKind::String(self.workspace.to_string_lossy().to_string())),
67-
)?
68-
.set_default("source.paths", Value::new(None, ValueKind::Array(vec![])))?
69-
.set_default("source.includes", Value::new(None, ValueKind::Array(vec![])))?
70-
.set_default("source.excludes", Value::new(None, ValueKind::Array(vec![])))?
71-
.set_default(
72-
"source.extensions",
73-
Value::new(None, ValueKind::Array(vec![Value::new(None, ValueKind::String("php".to_string()))])),
74-
)
75-
.map_err(Error::from)
76-
}
77-
78-
fn normalize(&mut self) -> Result<(), Error> {
67+
impl SourceConfiguration {
68+
pub fn normalize(&mut self) -> Result<(), Error> {
7969
// Make workspace absolute if not already
8070
let workspace =
8171
if !self.workspace.is_absolute() { (*CURRENT_DIR).join(&self.workspace) } else { self.workspace.clone() };
@@ -85,3 +75,7 @@ impl ConfigurationEntry for SourceConfiguration {
8575
Ok(())
8676
}
8777
}
78+
79+
fn default_extensions() -> Vec<String> {
80+
vec![PHP_EXTENSION.to_string()]
81+
}

src/consts.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ pub const ARCHIVE_EXTENSION: &str = "zip";
2121
#[cfg(not(target_os = "windows"))]
2222
pub const ARCHIVE_EXTENSION: &str = "tar.gz";
2323

24+
/// The extension for PHP files.
25+
pub const PHP_EXTENSION: &str = "php";
26+
2427
/// The name of the repository owner.
2528
pub const REPO_OWNER: &str = "carthage-software";
2629

0 commit comments

Comments
 (0)