harper_core/linting/lint_group/
flat_config.rs1use std::collections::BTreeMap;
2use std::hash::{Hash, Hasher};
3use std::mem;
4use std::sync::OnceLock;
5
6use hashbrown::HashMap;
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8
9use super::LintGroup;
10use crate::Dialect;
11use crate::spell::MutableDictionary;
12
13fn ser_ordered<S>(map: &HashMap<String, Option<bool>>, ser: S) -> Result<S::Ok, S::Error>
14where
15 S: Serializer,
16{
17 let ordered: BTreeMap<_, _> = map.iter().map(|(k, v)| (k.clone(), *v)).collect();
18 ordered.serialize(ser)
19}
20
21fn de_hashbrown<'de, D>(de: D) -> Result<HashMap<String, Option<bool>>, D::Error>
22where
23 D: Deserializer<'de>,
24{
25 let ordered: BTreeMap<String, Option<bool>> = BTreeMap::deserialize(de)?;
26 Ok(ordered.into_iter().collect())
27}
28
29#[derive(Debug, Serialize, Deserialize, Default, Clone, PartialEq, Eq)]
34#[serde(transparent)]
35pub struct FlatConfig {
36 #[serde(serialize_with = "ser_ordered", deserialize_with = "de_hashbrown")]
38 inner: HashMap<String, Option<bool>>,
39}
40
41impl FlatConfig {
42 fn curated() -> Self {
43 static CURATED: OnceLock<FlatConfig> = OnceLock::new();
44
45 CURATED
46 .get_or_init(|| {
47 let group =
49 LintGroup::new_curated(MutableDictionary::new().into(), Dialect::American);
50 group.config
51 })
52 .clone()
53 }
54
55 pub fn has_rule(&self, key: impl AsRef<str>) -> bool {
57 self.inner.contains_key(key.as_ref())
58 }
59
60 pub fn set_rule_enabled(&mut self, key: impl ToString, val: bool) {
61 self.inner.insert(key.to_string(), Some(val));
62 }
63
64 pub fn unset_rule_enabled(&mut self, key: impl AsRef<str>) {
67 self.inner.remove(key.as_ref());
68 }
69
70 pub fn set_rule_enabled_if_unset(&mut self, key: impl AsRef<str>, val: bool) {
71 if !self.inner.contains_key(key.as_ref()) {
72 self.set_rule_enabled(key.as_ref().to_string(), val);
73 }
74 }
75
76 pub fn is_rule_enabled(&self, key: &str) -> bool {
77 self.inner.get(key).cloned().flatten().unwrap_or(false)
78 }
79
80 pub fn clear(&mut self) {
83 for val in self.inner.values_mut() {
84 *val = None
85 }
86 }
87
88 pub fn merge_from(&mut self, other: FlatConfig) {
92 for (key, val) in other.inner {
93 if val.is_none() {
94 continue;
95 }
96
97 self.inner.insert(key.to_string(), val);
98 }
99 }
100
101 pub fn fill_with_curated(&mut self) {
103 let mut temp = Self::new_curated();
104 mem::swap(self, &mut temp);
105 self.merge_from(temp);
106 }
107
108 pub fn new_curated() -> Self {
109 Self::curated()
110 }
111}
112
113impl Hash for FlatConfig {
114 fn hash<H: Hasher>(&self, hasher: &mut H) {
115 for (key, value) in &self.inner {
116 hasher.write(key.as_bytes());
117 if let Some(value) = value {
118 hasher.write_u8(1);
119 hasher.write_u8(*value as u8);
120 } else {
121 hasher.write_u8(0);
123 hasher.write_u8(0);
124 }
125 }
126 }
127}