1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use super::{Token, TokenType};
use std::collections::VecDeque;
/// Removes tokens matching `,? "key": <type> ,?`., where `<type>` is a given
/// token type. Useful for removing `null` values, or all numbers, for instance.
/// Is made in a resilient fashion which doesn't require a sane input token stream.
pub struct FilterTypedKeyValuePairs<I: IntoIterator<Item = Token>> {
src: I::IntoIter,
// NOTE: We could remove the deck and keep the 3 slots we need as Option<Token>
// 0: optional comma
// 1: first string
// 2: colon
buf: VecDeque<Token>,
next_token: Option<Token>,
value_type: TokenType,
}
impl<I: IntoIterator<Item = Token>> FilterTypedKeyValuePairs<I> {
/// Returns a new `FilterTypedKeyValuePairs` instance from a `Token` iterator
pub fn new(src: I, value_type: TokenType) -> FilterTypedKeyValuePairs<I> {
FilterTypedKeyValuePairs {
src: src.into_iter(),
buf: VecDeque::with_capacity(3),
next_token: None,
value_type,
}
}
fn put_back(&mut self, t: Token) {
debug_assert!(self.next_token.is_none());
self.next_token = Some(t);
}
fn next_token(&mut self) -> Option<Token> {
match self.next_token.take() {
Some(t) => Some(t),
None => self.src.next(),
}
}
}
impl<I> Iterator for FilterTypedKeyValuePairs<I>
where
I: IntoIterator<Item = Token>,
{
type Item = Token;
fn next(&mut self) -> Option<Token> {
if !self.buf.is_empty() {
return self.buf.pop_front();
}
fn first_token(v: &mut VecDeque<Token>, t: Token) -> Option<Token> {
// assume it's a comma
if !v.is_empty() {
v.push_back(t);
v.pop_front()
} else {
Some(t)
}
}
let token = self.next_token();
match token {
Some(mut first_str_candidate) => {
loop {
match first_str_candidate.kind {
TokenType::String => {
let first_str_token = first_str_candidate;
match self.next_token() {
Some(colon_candidate) => {
if colon_candidate.kind == TokenType::Colon {
let colon = colon_candidate;
match self.next_token() {
Some(second_str_candidate) => {
if second_str_candidate.kind == self.value_type {
// WE HAVE A STR : STR triplete, and we forget it
// This works by just not putting it onto the ringbuffer
// See if there is a (optional) comma
// If self.buf has anything, it must be a comma !
// It is only 0 or 1 !
match self.next_token() {
Some(comma_candidate) => {
first_str_candidate = match match comma_candidate.kind {
TokenType::Comma => self.next_token(),
_ => {
self.buf.pop_front();
Some(comma_candidate)
}
} {
Some(t) => t,
None => return None,
};
continue;
}
None => return None,
}
} else {
let res = first_token(&mut self.buf, first_str_token);
self.buf.push_back(colon);
self.buf.push_back(second_str_candidate);
return res;
}
}
None => {
let res = first_token(&mut self.buf, first_str_token);
self.buf.push_back(colon);
return res;
}
}
} else {
let res = first_token(&mut self.buf, first_str_token);
self.buf.push_back(colon_candidate);
return res;
} // end is colon token
} // end have token (colon?)
None => return first_token(&mut self.buf, first_str_token),
} // end match next token (colon?)
} // end is string token,
TokenType::Comma => {
// NOTE: in case of malformed ,,,,, sequences, we just consider
// this a peek, return the previous comma, and put back this one
if !self.buf.is_empty() {
debug_assert_eq!(self.buf.len(), 1);
self.put_back(first_str_candidate);
return self.buf.pop_front();
}
match self.next_token() {
None => return Some(first_str_candidate),
Some(t) => {
// keep it, it will be returned first in case we
// end up not having a match
self.buf.push_back(first_str_candidate);
first_str_candidate = t;
continue;
}
}
}
_ => return first_token(&mut self.buf, first_str_candidate),
} // end match token kind (string?)
} // end inner str candidate LOOP
} // end have token
None => None,
}
}
}