use super::ConfigLevel;
use crate::config::{Config, CustomRule, ValidationConfig};
use crate::{Error, Result};
use serde::{Deserialize, Serialize};
use tokio::fs;
use tracing::debug;
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PartialConfig {
pub initialized: Option<bool>,
pub version: Option<String>,
pub update_channel: Option<String>,
pub auto_update: Option<bool>,
pub clippy_rules: Option<Vec<String>>,
pub max_file_lines: Option<usize>,
pub max_function_lines: Option<usize>,
pub required_edition: Option<String>,
pub required_rust_version: Option<String>,
pub ban_underscore_bandaid: Option<bool>,
pub require_documentation: Option<bool>,
pub custom_rules: Option<Vec<CustomRule>>,
}
impl PartialConfig {
pub async fn load_from_level(level: ConfigLevel) -> Result<Option<Self>> {
let path = level.path()?;
if !path.exists() {
debug!(
"No config found at {} level: {}",
level.display_name(),
path.display()
);
return Ok(None);
}
let contents = fs::read_to_string(&path).await.map_err(|e| {
Error::config(format!(
"Failed to read {} config: {}",
level.display_name(),
e
))
})?;
let partial: PartialConfig = toml::from_str(&contents).map_err(|e| {
Error::config(format!(
"Failed to parse {} config: {}",
level.display_name(),
e
))
})?;
tracing::info!(
"Loaded {} configuration from {}",
level.display_name(),
path.display()
);
Ok(Some(partial))
}
pub fn merge(mut self, other: PartialConfig) -> Self {
if other.initialized.is_some() {
self.initialized = other.initialized;
}
if other.version.is_some() {
self.version = other.version;
}
if other.update_channel.is_some() {
self.update_channel = other.update_channel;
}
if other.auto_update.is_some() {
self.auto_update = other.auto_update;
}
if other.clippy_rules.is_some() {
self.clippy_rules = other.clippy_rules;
}
if other.max_file_lines.is_some() {
self.max_file_lines = other.max_file_lines;
}
if other.max_function_lines.is_some() {
self.max_function_lines = other.max_function_lines;
}
if other.required_edition.is_some() {
self.required_edition = other.required_edition;
}
if other.required_rust_version.is_some() {
self.required_rust_version = other.required_rust_version;
}
if other.ban_underscore_bandaid.is_some() {
self.ban_underscore_bandaid = other.ban_underscore_bandaid;
}
if other.require_documentation.is_some() {
self.require_documentation = other.require_documentation;
}
if other.custom_rules.is_some() {
self.custom_rules = other.custom_rules;
}
self
}
pub fn to_full_config(self) -> Config {
let default = Config::default();
Config {
initialized: self.initialized.unwrap_or(default.initialized),
version: self.version.unwrap_or(default.version),
update_channel: self.update_channel.unwrap_or(default.update_channel),
auto_update: self.auto_update.unwrap_or(default.auto_update),
clippy_rules: self.clippy_rules.unwrap_or(default.clippy_rules),
max_file_lines: self.max_file_lines.unwrap_or(default.max_file_lines),
max_function_lines: self
.max_function_lines
.unwrap_or(default.max_function_lines),
required_edition: self.required_edition.unwrap_or(default.required_edition),
required_rust_version: self
.required_rust_version
.unwrap_or(default.required_rust_version),
ban_underscore_bandaid: self
.ban_underscore_bandaid
.unwrap_or(default.ban_underscore_bandaid),
require_documentation: self
.require_documentation
.unwrap_or(default.require_documentation),
custom_rules: self.custom_rules.unwrap_or(default.custom_rules),
validation: ValidationConfig::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_partial_config_merge() {
let base = PartialConfig {
max_file_lines: Some(300),
max_function_lines: Some(50),
..Default::default()
};
let override_config = PartialConfig {
max_file_lines: Some(400),
required_edition: Some("2021".to_string()),
..Default::default()
};
let merged = base.merge(override_config);
assert_eq!(merged.max_file_lines, Some(400));
assert_eq!(merged.max_function_lines, Some(50));
assert_eq!(merged.required_edition, Some("2021".to_string()));
}
#[test]
fn test_partial_to_full_config() {
let partial = PartialConfig {
max_file_lines: Some(500),
..Default::default()
};
let full = partial.to_full_config();
assert_eq!(full.max_file_lines, 500);
assert_eq!(full.max_function_lines, 50); }
}