1use std::{cell::RefCell, collections::HashSet};
2
3use crate::{impl_cache, ConfigError};
4
5pub type ConfigKey<'a> = CacheKey<'a>;
41
42#[derive(Debug)]
43pub(crate) struct CacheString {
44 current: String,
45 mark: usize,
46}
47thread_local! {
48 static BUG: RefCell<CacheString> = RefCell::new(CacheString::new());
49}
50impl CacheString {
51 pub(crate) fn new() -> CacheString {
52 Self {
53 current: String::with_capacity(10),
54 mark: 0,
55 }
56 }
57
58 #[inline]
59 #[allow(single_use_lifetimes)]
60 pub(crate) fn push<'a, I: IntoIterator<Item = PartialKey<'a>>>(&mut self, iter: I) -> usize {
61 let mark = self.mark;
62 self.mark = self.current.len();
63 for i in iter {
64 i.update_string(&mut self.current);
65 }
66 mark
67 }
68
69 #[inline]
70 fn pop(&mut self, mark: usize) {
71 self.current.truncate(self.mark);
72 self.mark = mark;
73 }
74
75 #[inline]
76 fn clear(&mut self) {
77 self.current.clear();
78 self.mark = 0;
79 }
80
81 #[inline]
82 pub(crate) fn new_key(&mut self) -> CacheKey<'_> {
83 CacheKey { cache: self }
84 }
85
86 #[inline]
87 pub(crate) fn with_key_place<T, F: FnMut(&mut Self) -> Result<T, ConfigError>>(
88 f: F,
89 ) -> Result<T, ConfigError> {
90 BUG.with(move |buf| Self::with_key_buf(buf, f))
91 }
92}
93
94impl_cache!(CacheString);
95
96#[derive(Debug)]
98pub struct CacheKey<'a> {
99 cache: &'a mut CacheString,
100}
101
102impl Drop for CacheKey<'_> {
103 fn drop(&mut self) {
104 self.cache.clear();
105 }
106}
107
108impl<'a> CacheKey<'a> {
109 pub(crate) fn push<I: Into<PartialKeyIter<'a>>>(&mut self, iter: I) -> usize {
110 self.cache.push(iter.into())
111 }
112 pub(crate) fn pop(&mut self, mark: usize) {
113 self.cache.pop(mark);
114 }
115
116 pub(crate) fn as_str(&self) -> &str {
118 &self.cache.current
119 }
120}
121impl std::fmt::Display for CacheKey<'_> {
122 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123 write!(f, "{}", self.as_str())
124 }
125}
126#[allow(single_use_lifetimes)]
128#[derive(Debug, PartialEq, Eq)]
129pub enum PartialKey<'a> {
130 Str(&'a str),
132 Int(usize),
134}
135
136impl PartialKey<'_> {
137 #[inline]
138 pub(crate) fn update_string(&self, key_long: &mut String) {
139 match self {
140 PartialKey::Int(i) => {
141 key_long.push('[');
142 key_long.push_str(&i.to_string());
143 key_long.push(']');
144 }
145 PartialKey::Str(v) => {
146 if !key_long.is_empty() {
147 key_long.push('.');
148 }
149 key_long.push_str(v);
150 }
151 }
152 }
153}
154
155#[derive(Debug)]
156#[allow(variant_size_differences)]
157pub enum PartialKeyIter<'a> {
158 Str(std::str::Split<'a, &'a [char]>),
159 Int(Option<usize>),
160}
161
162impl<'a> Iterator for PartialKeyIter<'a> {
163 type Item = PartialKey<'a>;
164 fn next(&mut self) -> Option<Self::Item> {
165 match self {
166 PartialKeyIter::Str(s) => {
167 for v in s {
168 if v.is_empty() {
169 continue;
170 }
171 return Some(if let Ok(i) = v.parse() {
172 PartialKey::Int(i)
173 } else {
174 PartialKey::Str(v)
175 });
176 }
177 None
178 }
179 PartialKeyIter::Int(x) => x.take().map(|x| x.into()),
180 }
181 }
182}
183
184#[derive(Debug)]
186pub struct PartialKeyCollector<'a> {
187 pub(crate) str_key: HashSet<&'a str>,
188 pub(crate) int_key: Option<usize>,
189}
190
191#[allow(single_use_lifetimes)]
192impl PartialKeyCollector<'_> {
193 pub(crate) fn new() -> Self {
194 Self {
195 str_key: HashSet::new(),
196 int_key: None,
197 }
198 }
199
200 pub(crate) fn insert_int(&mut self, key: usize) {
202 if let Some(u) = self.int_key {
203 if u > key {
204 return;
205 }
206 }
207 self.int_key = Some(key + 1);
208 }
209}
210
211impl<'a> From<&'a str> for PartialKeyIter<'a> {
212 fn from(s: &'a str) -> Self {
213 PartialKeyIter::Str(s.split(&['.', '[', ']'][..]))
214 }
215}
216
217impl From<usize> for PartialKey<'_> {
218 fn from(s: usize) -> Self {
219 PartialKey::Int(s)
220 }
221}
222
223impl From<usize> for PartialKeyIter<'_> {
224 fn from(s: usize) -> Self {
225 PartialKeyIter::Int(Some(s))
226 }
227}
228
229impl<'a> From<&'a String> for PartialKeyIter<'a> {
230 fn from(s: &'a String) -> Self {
231 s.as_str().into()
232 }
233}
234
235#[cfg_attr(coverage_nightly, coverage(off))]
236#[cfg(test)]
237mod test {
238 use super::*;
239
240 macro_rules! should_eq {
241 ($origin:expr => $norm:expr) => {
242 let mut che = CacheString::new();
243 let mut key = che.new_key();
244 key.push($origin);
245 assert_eq!(&key.to_string(), $norm);
246 let mut chd = CacheString::new();
247 let mut kez = chd.new_key();
248 kez.push($norm);
249 assert_eq!(true, key.as_str() == kez.as_str());
250 };
251 }
252
253 #[test]
254 fn key_test() {
255 should_eq!("" => "");
256 should_eq!("." => "");
257 should_eq!(".." => "");
258 should_eq!("[" => "");
259 should_eq!("[1]" => "[1]");
260 should_eq!("1" => "[1]");
261 should_eq!("1[1]" => "[1][1]");
262 should_eq!("prefix.prop" => "prefix.prop");
263 should_eq!(".prefix.prop"=> "prefix.prop");
264 should_eq!("[]prefix.prop"=> "prefix.prop");
265 should_eq!("[0]prefix.prop"=> "[0].prefix.prop");
266 should_eq!("prefix[0].prop"=> "prefix[0].prop");
267 should_eq!("prefix.0.prop"=> "prefix[0].prop");
268 should_eq!("hello" => "hello");
269 }
270
271 macro_rules! should_ls {
272 ($($origin:literal => $norm:literal,)+) => {
273 let mut che = CacheString::new();
274 let mut key = che.new_key();
275 let mut vec = vec![];
276 let mut xxx = vec![];
277 $(
278 xxx.push(key.push($origin));
279 assert_eq!($norm, key.as_str());
280 vec.push(key.to_string());
281 )+
282 while let Some(v) = vec.pop() {
283 assert_eq!(&v, key.as_str());
284 key.pop(xxx.pop().unwrap());
285 }
286 };
287 }
288
289 #[test]
290 fn key_push_test() {
291 should_ls!(
292 "a" => "a",
293 "" => "a",
294 "b" => "a.b",
295 "1" => "a.b[1]",
296 "1" => "a.b[1][1]",
297 "a.1" => "a.b[1][1].a[1]",
298 );
299 }
300}