1use crate::types::LineLength;
2use indexmap::IndexMap;
3use std::collections::BTreeMap;
4use std::collections::HashMap;
5use std::marker::PhantomData;
6
7use super::flavor::{ConfigLoaded, MarkdownFlavor};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum ConfigSource {
19 Default,
21 UserConfig,
23 PyprojectToml,
25 ProjectConfig,
27 Cli,
29}
30
31#[derive(Debug, Clone)]
32pub struct ConfigOverride<T> {
33 pub value: T,
34 pub source: ConfigSource,
35 pub file: Option<String>,
36 pub line: Option<usize>,
37}
38
39#[derive(Debug, Clone)]
40pub struct SourcedValue<T> {
41 pub value: T,
42 pub source: ConfigSource,
43 pub overrides: Vec<ConfigOverride<T>>,
44}
45
46impl<T: Clone> SourcedValue<T> {
47 pub fn new(value: T, source: ConfigSource) -> Self {
48 Self {
49 value: value.clone(),
50 source,
51 overrides: vec![ConfigOverride {
52 value,
53 source,
54 file: None,
55 line: None,
56 }],
57 }
58 }
59
60 pub fn merge_override(
64 &mut self,
65 new_value: T,
66 new_source: ConfigSource,
67 new_file: Option<String>,
68 new_line: Option<usize>,
69 ) {
70 fn source_precedence(src: ConfigSource) -> u8 {
72 match src {
73 ConfigSource::Default => 0,
74 ConfigSource::UserConfig => 1,
75 ConfigSource::PyprojectToml => 2,
76 ConfigSource::ProjectConfig => 3,
77 ConfigSource::Cli => 4,
78 }
79 }
80
81 if source_precedence(new_source) >= source_precedence(self.source) {
82 self.value = new_value.clone();
83 self.source = new_source;
84 self.overrides.push(ConfigOverride {
85 value: new_value,
86 source: new_source,
87 file: new_file,
88 line: new_line,
89 });
90 }
91 }
92
93 pub fn push_override(&mut self, value: T, source: ConfigSource, file: Option<String>, line: Option<usize>) {
94 self.value = value.clone();
97 self.source = source;
98 self.overrides.push(ConfigOverride {
99 value,
100 source,
101 file,
102 line,
103 });
104 }
105}
106
107impl<T: Clone + Eq + std::hash::Hash> SourcedValue<Vec<T>> {
108 pub fn merge_union(
111 &mut self,
112 new_value: Vec<T>,
113 new_source: ConfigSource,
114 new_file: Option<String>,
115 new_line: Option<usize>,
116 ) {
117 fn source_precedence(src: ConfigSource) -> u8 {
118 match src {
119 ConfigSource::Default => 0,
120 ConfigSource::UserConfig => 1,
121 ConfigSource::PyprojectToml => 2,
122 ConfigSource::ProjectConfig => 3,
123 ConfigSource::Cli => 4,
124 }
125 }
126
127 if source_precedence(new_source) >= source_precedence(self.source) {
128 let mut combined = self.value.clone();
130 for item in new_value.iter() {
131 if !combined.contains(item) {
132 combined.push(item.clone());
133 }
134 }
135
136 self.value = combined;
137 self.source = new_source;
138 self.overrides.push(ConfigOverride {
139 value: new_value,
140 source: new_source,
141 file: new_file,
142 line: new_line,
143 });
144 }
145 }
146}
147
148#[derive(Debug, Clone)]
149pub struct SourcedGlobalConfig {
150 pub enable: SourcedValue<Vec<String>>,
151 pub disable: SourcedValue<Vec<String>>,
152 pub exclude: SourcedValue<Vec<String>>,
153 pub include: SourcedValue<Vec<String>>,
154 pub respect_gitignore: SourcedValue<bool>,
155 pub line_length: SourcedValue<LineLength>,
156 pub output_format: Option<SourcedValue<String>>,
157 pub fixable: SourcedValue<Vec<String>>,
158 pub unfixable: SourcedValue<Vec<String>>,
159 pub flavor: SourcedValue<MarkdownFlavor>,
160 pub force_exclude: SourcedValue<bool>,
161 pub cache_dir: Option<SourcedValue<String>>,
162 pub cache: SourcedValue<bool>,
163}
164
165impl Default for SourcedGlobalConfig {
166 fn default() -> Self {
167 SourcedGlobalConfig {
168 enable: SourcedValue::new(Vec::new(), ConfigSource::Default),
169 disable: SourcedValue::new(Vec::new(), ConfigSource::Default),
170 exclude: SourcedValue::new(Vec::new(), ConfigSource::Default),
171 include: SourcedValue::new(Vec::new(), ConfigSource::Default),
172 respect_gitignore: SourcedValue::new(true, ConfigSource::Default),
173 line_length: SourcedValue::new(LineLength::default(), ConfigSource::Default),
174 output_format: None,
175 fixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
176 unfixable: SourcedValue::new(Vec::new(), ConfigSource::Default),
177 flavor: SourcedValue::new(MarkdownFlavor::default(), ConfigSource::Default),
178 force_exclude: SourcedValue::new(false, ConfigSource::Default),
179 cache_dir: None,
180 cache: SourcedValue::new(true, ConfigSource::Default),
181 }
182 }
183}
184
185#[derive(Debug, Default, Clone)]
186pub struct SourcedRuleConfig {
187 pub severity: Option<SourcedValue<crate::rule::Severity>>,
188 pub values: BTreeMap<String, SourcedValue<toml::Value>>,
189}
190
191#[derive(Debug, Clone)]
194pub struct SourcedConfigFragment {
195 pub global: SourcedGlobalConfig,
196 pub per_file_ignores: SourcedValue<HashMap<String, Vec<String>>>,
197 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
198 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
199 pub rules: BTreeMap<String, SourcedRuleConfig>,
200 pub unknown_keys: Vec<(String, String, Option<String>)>, }
203
204impl Default for SourcedConfigFragment {
205 fn default() -> Self {
206 Self {
207 global: SourcedGlobalConfig::default(),
208 per_file_ignores: SourcedValue::new(HashMap::new(), ConfigSource::Default),
209 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
210 code_block_tools: SourcedValue::new(
211 crate::code_block_tools::CodeBlockToolsConfig::default(),
212 ConfigSource::Default,
213 ),
214 rules: BTreeMap::new(),
215 unknown_keys: Vec::new(),
216 }
217 }
218}
219
220#[derive(Debug, Clone)]
222pub struct ConfigValidationWarning {
223 pub message: String,
224 pub rule: Option<String>,
225 pub key: Option<String>,
226}
227
228#[derive(Debug, Clone)]
246pub struct SourcedConfig<State = ConfigLoaded> {
247 pub global: SourcedGlobalConfig,
248 pub per_file_ignores: SourcedValue<HashMap<String, Vec<String>>>,
249 pub per_file_flavor: SourcedValue<IndexMap<String, MarkdownFlavor>>,
250 pub code_block_tools: SourcedValue<crate::code_block_tools::CodeBlockToolsConfig>,
251 pub rules: BTreeMap<String, SourcedRuleConfig>,
252 pub loaded_files: Vec<String>,
253 pub unknown_keys: Vec<(String, String, Option<String>)>, pub project_root: Option<std::path::PathBuf>,
256 pub validation_warnings: Vec<ConfigValidationWarning>,
258 pub(super) _state: PhantomData<State>,
260}
261
262impl Default for SourcedConfig<ConfigLoaded> {
263 fn default() -> Self {
264 Self {
265 global: SourcedGlobalConfig::default(),
266 per_file_ignores: SourcedValue::new(HashMap::new(), ConfigSource::Default),
267 per_file_flavor: SourcedValue::new(IndexMap::new(), ConfigSource::Default),
268 code_block_tools: SourcedValue::new(
269 crate::code_block_tools::CodeBlockToolsConfig::default(),
270 ConfigSource::Default,
271 ),
272 rules: BTreeMap::new(),
273 loaded_files: Vec::new(),
274 unknown_keys: Vec::new(),
275 project_root: None,
276 validation_warnings: Vec::new(),
277 _state: PhantomData,
278 }
279 }
280}