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)]
17pub enum ConfigSource {
18 Default,
20 UserConfig,
22 PyprojectToml,
24 ProjectConfig,
26 Cli,
28}
29
30#[derive(Debug, Clone)]
31pub struct ConfigOverride<T> {
32 pub value: T,
33 pub source: ConfigSource,
34 pub file: Option<String>,
35 pub line: Option<usize>,
36}
37
38#[derive(Debug, Clone)]
39pub struct SourcedValue<T> {
40 pub value: T,
41 pub source: ConfigSource,
42 pub overrides: Vec<ConfigOverride<T>>,
43}
44
45impl<T: Clone> SourcedValue<T> {
46 pub fn new(value: T, source: ConfigSource) -> Self {
47 Self {
48 value: value.clone(),
49 source,
50 overrides: vec![ConfigOverride {
51 value,
52 source,
53 file: None,
54 line: None,
55 }],
56 }
57 }
58
59 pub fn merge_override(
63 &mut self,
64 new_value: T,
65 new_source: ConfigSource,
66 new_file: Option<String>,
67 new_line: Option<usize>,
68 ) {
69 fn source_precedence(src: ConfigSource) -> u8 {
71 match src {
72 ConfigSource::Default => 0,
73 ConfigSource::UserConfig => 1,
74 ConfigSource::PyprojectToml => 2,
75 ConfigSource::ProjectConfig => 3,
76 ConfigSource::Cli => 4,
77 }
78 }
79
80 if source_precedence(new_source) >= source_precedence(self.source) {
81 self.value = new_value.clone();
82 self.source = new_source;
83 self.overrides.push(ConfigOverride {
84 value: new_value,
85 source: new_source,
86 file: new_file,
87 line: new_line,
88 });
89 }
90 }
91
92 pub fn push_override(&mut self, value: T, source: ConfigSource, file: Option<String>, line: Option<usize>) {
93 self.value = value.clone();
96 self.source = source;
97 self.overrides.push(ConfigOverride {
98 value,
99 source,
100 file,
101 line,
102 });
103 }
104}
105
106impl<T: Clone + Eq + std::hash::Hash> SourcedValue<Vec<T>> {
107 pub fn merge_union(
110 &mut self,
111 new_value: Vec<T>,
112 new_source: ConfigSource,
113 new_file: Option<String>,
114 new_line: Option<usize>,
115 ) {
116 fn source_precedence(src: ConfigSource) -> u8 {
117 match src {
118 ConfigSource::Default => 0,
119 ConfigSource::UserConfig => 1,
120 ConfigSource::PyprojectToml => 2,
121 ConfigSource::ProjectConfig => 3,
122 ConfigSource::Cli => 4,
123 }
124 }
125
126 if source_precedence(new_source) >= source_precedence(self.source) {
127 let mut combined = self.value.clone();
129 for item in &new_value {
130 if !combined.contains(item) {
131 combined.push(item.clone());
132 }
133 }
134
135 self.value = combined;
136 self.source = new_source;
137 self.overrides.push(ConfigOverride {
138 value: new_value,
139 source: new_source,
140 file: new_file,
141 line: new_line,
142 });
143 }
144 }
145}
146
147#[derive(Debug, Clone)]
148pub struct SourcedGlobalConfig {
149 pub enable: SourcedValue<Vec<String>>,
150 pub disable: SourcedValue<Vec<String>>,
151 pub exclude: SourcedValue<Vec<String>>,
152 pub include: SourcedValue<Vec<String>>,
153 pub respect_gitignore: SourcedValue<bool>,
154 pub line_length: SourcedValue<LineLength>,
155 pub output_format: Option<SourcedValue<String>>,
156 pub fixable: SourcedValue<Vec<String>>,
157 pub unfixable: SourcedValue<Vec<String>>,
158 pub flavor: SourcedValue<MarkdownFlavor>,
159 pub force_exclude: SourcedValue<bool>,
160 pub cache_dir: Option<SourcedValue<String>>,
161 pub cache: SourcedValue<bool>,
162 pub extend_enable: SourcedValue<Vec<String>>,
163 pub extend_disable: SourcedValue<Vec<String>>,
164}
165
166impl Default for SourcedGlobalConfig {
167 fn default() -> Self {
168 SourcedGlobalConfig {
169 enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
170 disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
171 exclude: SourcedValue::new(Vec::new(), ConfigSource::Default),
172 include: SourcedValue::new(Vec::new(), ConfigSource::Default),
173 respect_gitignore: SourcedValue::new(true, ConfigSource::Default),
174 line_length: SourcedValue::new(LineLength::default(), ConfigSource::Default),
175 output_format: None,
176 fixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
177 unfixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
178 flavor: SourcedValue::new(MarkdownFlavor::default(), ConfigSource::Default),
179 force_exclude: SourcedValue::new(false, ConfigSource::Default),
180 cache_dir: None,
181 cache: SourcedValue::new(true, ConfigSource::Default),
182 extend_enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
183 extend_disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
184 }
185 }
186}
187
188#[derive(Debug, Default, Clone)]
189pub struct SourcedRuleConfig {
190 pub severity: Option<SourcedValue<crate::rule::Severity>>,
191 pub values: BTreeMap<String, SourcedValue<toml::Value>>,
192}
193
194#[derive(Debug, Clone)]
197pub struct SourcedConfigFragment {
198 pub extends: Option<String>,
200 pub global: SourcedGlobalConfig,
201 pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
202 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
203 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
204 pub rules: BTreeMap<String, SourcedRuleConfig>,
205 pub rule_display_names: HashMap<String, String>,
209 pub unknown_keys: Vec<(String, String, Option<String>)>, }
212
213impl Default for SourcedConfigFragment {
214 fn default() -> Self {
215 Self {
216 extends: None,
217 global: SourcedGlobalConfig::default(),
218 per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
219 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
220 code_block_tools: SourcedValue::new(
221 crate::code_block_tools::CodeBlockToolsConfig::default(),
222 ConfigSource::Default,
223 ),
224 rules: BTreeMap::new(),
225 rule_display_names: HashMap::new(),
226 unknown_keys: Vec::new(),
227 }
228 }
229}
230
231#[derive(Debug, Clone)]
233pub struct ConfigValidationWarning {
234 pub message: String,
235 pub rule: Option<String>,
236 pub key: Option<String>,
237}
238
239#[derive(Debug, Clone)]
257pub struct SourcedConfig<State = ConfigLoaded> {
258 pub global: SourcedGlobalConfig,
259 pub per_file_ignores: SourcedValue<BTreeMap<String, Vec<String>>>,
260 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
261 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
262 pub rules: BTreeMap<String, SourcedRuleConfig>,
263 pub loaded_files: Vec<String>,
264 pub unknown_keys: Vec<(String, String, Option<String>)>, pub project_root: Option<std::path::PathBuf>,
267 pub validation_warnings: Vec<ConfigValidationWarning>,
269 pub(super) _state: PhantomData<State>,
271}
272
273impl Default for SourcedConfig<ConfigLoaded> {
274 fn default() -> Self {
275 Self {
276 global: SourcedGlobalConfig::default(),
277 per_file_ignores: SourcedValue::new(BTreeMap::new(), ConfigSource::Default),
278 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
279 code_block_tools: SourcedValue::new(
280 crate::code_block_tools::CodeBlockToolsConfig::default(),
281 ConfigSource::Default,
282 ),
283 rules: BTreeMap::new(),
284 loaded_files: Vec::new(),
285 unknown_keys: Vec::new(),
286 project_root: None,
287 validation_warnings: Vec::new(),
288 _state: PhantomData,
289 }
290 }
291}