1use crate::types::LineLength;
2use indexmap::IndexMap;
3use std::collections::{BTreeMap, HashMap};
4use std::marker::PhantomData;
5
6use super::flavor::{ConfigLoaded, MarkdownFlavor};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum ConfigSource {
23 Default,
25 EditorConfig,
27 UserConfig,
29 PyprojectToml,
31 ProjectConfig,
33 Cli,
35}
36
37fn source_precedence(src: ConfigSource) -> u8 {
38 match src {
39 ConfigSource::Default => 0,
40 ConfigSource::EditorConfig => 1,
41 ConfigSource::UserConfig => 2,
42 ConfigSource::PyprojectToml => 3,
43 ConfigSource::ProjectConfig => 4,
44 ConfigSource::Cli => 5,
45 }
46}
47
48#[derive(Debug, Clone)]
53pub struct SourcedValue<T> {
54 pub value: T,
55 pub source: ConfigSource,
56 pub origin: Option<String>,
59}
60
61impl<T: Clone> SourcedValue<T> {
62 pub fn new(value: T, source: ConfigSource) -> Self {
63 Self {
64 value,
65 source,
66 origin: None,
67 }
68 }
69
70 pub fn merge_override(&mut self, new_value: T, new_source: ConfigSource, new_origin: Option<String>) {
74 if source_precedence(new_source) >= source_precedence(self.source) {
75 self.value = new_value;
76 self.source = new_source;
77 self.origin = new_origin;
78 }
79 }
80
81 pub fn merge_from(&mut self, other: SourcedValue<T>) {
83 self.merge_override(other.value, other.source, other.origin);
84 }
85
86 pub fn push_override(&mut self, value: T, source: ConfigSource, origin: Option<String>) {
90 self.value = value;
91 self.source = source;
92 self.origin = origin;
93 }
94}
95
96impl<T: Clone + Eq + std::hash::Hash> SourcedValue<Vec<T>> {
97 pub fn merge_union(&mut self, new_value: Vec<T>, new_source: ConfigSource, new_origin: Option<String>) {
101 if source_precedence(new_source) >= source_precedence(self.source) {
102 for item in new_value {
103 if !self.value.contains(&item) {
104 self.value.push(item);
105 }
106 }
107 self.source = new_source;
108 self.origin = new_origin;
109 }
110 }
111
112 pub fn merge_union_from(&mut self, other: SourcedValue<Vec<T>>) {
114 self.merge_union(other.value, other.source, other.origin);
115 }
116}
117
118#[derive(Debug, Clone)]
119pub struct SourcedGlobalConfig {
120 pub enable: SourcedValue<Vec<String>>,
121 pub disable: SourcedValue<Vec<String>>,
122 pub exclude: SourcedValue<Vec<String>>,
123 pub include: SourcedValue<Vec<String>>,
124 pub respect_gitignore: SourcedValue<bool>,
125 pub line_length: SourcedValue<LineLength>,
126 pub output_format: Option<SourcedValue<String>>,
127 pub fixable: SourcedValue<Vec<String>>,
128 pub unfixable: SourcedValue<Vec<String>>,
129 pub flavor: SourcedValue<MarkdownFlavor>,
130 pub force_exclude: SourcedValue<bool>,
131 pub cache_dir: Option<SourcedValue<String>>,
132 pub cache: SourcedValue<bool>,
133 pub extend_enable: SourcedValue<Vec<String>>,
134 pub extend_disable: SourcedValue<Vec<String>>,
135 pub editorconfig: SourcedValue<bool>,
136}
137
138impl Default for SourcedGlobalConfig {
139 fn default() -> Self {
140 SourcedGlobalConfig {
141 enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
142 disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
143 exclude: SourcedValue::new(Vec::new(), ConfigSource::Default),
144 include: SourcedValue::new(Vec::new(), ConfigSource::Default),
145 respect_gitignore: SourcedValue::new(true, ConfigSource::Default),
146 line_length: SourcedValue::new(LineLength::default(), ConfigSource::Default),
147 output_format: None,
148 fixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
149 unfixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
150 flavor: SourcedValue::new(MarkdownFlavor::default(), ConfigSource::Default),
151 force_exclude: SourcedValue::new(false, ConfigSource::Default),
152 cache_dir: None,
153 cache: SourcedValue::new(true, ConfigSource::Default),
154 extend_enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
155 extend_disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
156 editorconfig: SourcedValue::new(false, ConfigSource::Default),
157 }
158 }
159}
160
161#[derive(Debug, Default, Clone)]
162pub struct SourcedRuleConfig {
163 pub severity: Option<SourcedValue<crate::rule::Severity>>,
164 pub values: BTreeMap<String, SourcedValue<toml::Value>>,
165}
166
167#[derive(Debug, Clone)]
170pub struct SourcedConfigFragment {
171 pub extends: Option<String>,
173 pub global: SourcedGlobalConfig,
174 pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
175 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
176 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
177 pub rules: BTreeMap<String, SourcedRuleConfig>,
178 pub rule_display_names: HashMap<String, String>,
182 pub unknown_keys: Vec<(String, String, Option<String>)>, }
185
186impl Default for SourcedConfigFragment {
187 fn default() -> Self {
188 Self {
189 extends: None,
190 global: SourcedGlobalConfig::default(),
191 per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
192 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
193 code_block_tools: SourcedValue::new(
194 crate::code_block_tools::CodeBlockToolsConfig::default(),
195 ConfigSource::Default,
196 ),
197 rules: BTreeMap::new(),
198 rule_display_names: HashMap::new(),
199 unknown_keys: Vec::new(),
200 }
201 }
202}
203
204#[derive(Debug, Clone)]
206pub struct ConfigValidationWarning {
207 pub message: String,
208 pub rule: Option<String>,
209 pub key: Option<String>,
210}
211
212#[derive(Debug, Clone)]
230pub struct SourcedConfig<State = ConfigLoaded> {
231 pub global: SourcedGlobalConfig,
232 pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
233 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
234 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
235 pub rules: BTreeMap<String, SourcedRuleConfig>,
236 pub loaded_files: Vec<String>,
237 pub unknown_keys: Vec<(String, String, Option<String>)>, pub project_root: Option<std::path::PathBuf>,
240 pub discovery_warnings: Vec<String>,
244 pub validation_warnings: Vec<ConfigValidationWarning>,
246 pub(super) _state: PhantomData<State>,
248}
249
250impl Default for SourcedConfig<ConfigLoaded> {
251 fn default() -> Self {
252 Self {
253 global: SourcedGlobalConfig::default(),
254 per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
255 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
256 code_block_tools: SourcedValue::new(
257 crate::code_block_tools::CodeBlockToolsConfig::default(),
258 ConfigSource::Default,
259 ),
260 rules: BTreeMap::new(),
261 loaded_files: Vec::new(),
262 unknown_keys: Vec::new(),
263 project_root: None,
264 discovery_warnings: Vec::new(),
265 validation_warnings: Vec::new(),
266 _state: PhantomData,
267 }
268 }
269}