use crate::types::LineLength;
use indexmap::IndexMap;
use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::marker::PhantomData;
use super::flavor::{ConfigLoaded, MarkdownFlavor};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfigSource {
Default,
EditorConfig,
UserConfig,
PyprojectToml,
ProjectConfig,
Cli,
}
fn source_precedence(src: ConfigSource) -> u8 {
match src {
ConfigSource::Default => 0,
ConfigSource::EditorConfig => 1,
ConfigSource::UserConfig => 2,
ConfigSource::PyprojectToml => 3,
ConfigSource::ProjectConfig => 4,
ConfigSource::Cli => 5,
}
}
#[derive(Debug, Clone)]
pub struct SourcedValue<T> {
pub value: T,
pub source: ConfigSource,
pub origin: Option<String>,
}
impl<T: Clone> SourcedValue<T> {
pub fn new(value: T, source: ConfigSource) -> Self {
Self {
value,
source,
origin: None,
}
}
pub fn merge_override(&mut self, new_value: T, new_source: ConfigSource, new_origin: Option<String>) -> bool {
if source_precedence(new_source) >= source_precedence(self.source) {
self.value = new_value;
self.source = new_source;
self.origin = new_origin;
true
} else {
false
}
}
pub fn merge_from(&mut self, other: SourcedValue<T>) -> bool {
self.merge_override(other.value, other.source, other.origin)
}
pub fn push_override(&mut self, value: T, source: ConfigSource, origin: Option<String>) {
self.value = value;
self.source = source;
self.origin = origin;
}
}
impl<T: Clone + Eq + std::hash::Hash> SourcedValue<Vec<T>> {
pub fn merge_union(&mut self, new_value: Vec<T>, new_source: ConfigSource, new_origin: Option<String>) {
if source_precedence(new_source) >= source_precedence(self.source) {
for item in new_value {
if !self.value.contains(&item) {
self.value.push(item);
}
}
self.source = new_source;
self.origin = new_origin;
}
}
pub fn merge_union_from(&mut self, other: SourcedValue<Vec<T>>) {
self.merge_union(other.value, other.source, other.origin);
}
}
#[derive(Debug, Clone)]
pub struct SourcedGlobalConfig {
pub enable: SourcedValue<Vec<String>>,
pub disable: SourcedValue<Vec<String>>,
pub exclude: SourcedValue<Vec<String>>,
pub include: SourcedValue<Vec<String>>,
pub include_withheld: Option<String>,
pub respect_gitignore: SourcedValue<bool>,
pub line_length: SourcedValue<LineLength>,
pub output_format: Option<SourcedValue<String>>,
pub fixable: SourcedValue<Vec<String>>,
pub unfixable: SourcedValue<Vec<String>>,
pub flavor: SourcedValue<MarkdownFlavor>,
pub force_exclude: SourcedValue<bool>,
pub cache_dir: Option<SourcedValue<String>>,
pub cache: SourcedValue<bool>,
pub extend_enable: SourcedValue<Vec<String>>,
pub extend_disable: SourcedValue<Vec<String>>,
pub editorconfig: SourcedValue<bool>,
}
impl Default for SourcedGlobalConfig {
fn default() -> Self {
SourcedGlobalConfig {
enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
exclude: SourcedValue::new(Vec::new(), ConfigSource::Default),
include: SourcedValue::new(Vec::new(), ConfigSource::Default),
include_withheld: None,
respect_gitignore: SourcedValue::new(true, ConfigSource::Default),
line_length: SourcedValue::new(LineLength::default(), ConfigSource::Default),
output_format: None,
fixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
unfixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
flavor: SourcedValue::new(MarkdownFlavor::default(), ConfigSource::Default),
force_exclude: SourcedValue::new(false, ConfigSource::Default),
cache_dir: None,
cache: SourcedValue::new(true, ConfigSource::Default),
extend_enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
extend_disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
editorconfig: SourcedValue::new(false, ConfigSource::Default),
}
}
}
#[derive(Debug, Default, Clone)]
pub struct SourcedRuleConfig {
pub severity: Option<SourcedValue<crate::rule::Severity>>,
pub values: BTreeMap<String, SourcedValue<toml::Value>>,
pub withheld_keys: BTreeSet<String>,
}
#[derive(Debug, Clone)]
pub struct SourcedConfigFragment {
pub extends: Option<String>,
pub global: SourcedGlobalConfig,
pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
pub rules: BTreeMap<String, SourcedRuleConfig>,
pub rule_display_names: HashMap<String, String>,
pub unknown_keys: Vec<(String, String, Option<String>)>,
pub load_warnings: Vec<String>,
}
impl Default for SourcedConfigFragment {
fn default() -> Self {
Self {
extends: None,
global: SourcedGlobalConfig::default(),
per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
code_block_tools: SourcedValue::new(
crate::code_block_tools::CodeBlockToolsConfig::default(),
ConfigSource::Default,
),
rules: BTreeMap::new(),
rule_display_names: HashMap::new(),
unknown_keys: Vec::new(),
load_warnings: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct ConfigValidationWarning {
pub message: String,
pub rule: Option<String>,
pub key: Option<String>,
}
#[derive(Debug, Clone)]
pub struct SourcedConfig<State = ConfigLoaded> {
pub global: SourcedGlobalConfig,
pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
pub rules: BTreeMap<String, SourcedRuleConfig>,
pub loaded_files: Vec<String>,
pub unknown_keys: Vec<(String, String, Option<String>)>,
pub project_root: Option<std::path::PathBuf>,
pub discovery_warnings: Vec<String>,
pub validation_warnings: Vec<ConfigValidationWarning>,
pub(super) _state: PhantomData<State>,
}
impl Default for SourcedConfig<ConfigLoaded> {
fn default() -> Self {
Self {
global: SourcedGlobalConfig::default(),
per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
code_block_tools: SourcedValue::new(
crate::code_block_tools::CodeBlockToolsConfig::default(),
ConfigSource::Default,
),
rules: BTreeMap::new(),
loaded_files: Vec::new(),
unknown_keys: Vec::new(),
project_root: None,
discovery_warnings: Vec::new(),
validation_warnings: Vec::new(),
_state: PhantomData,
}
}
}