1use serde::{Deserialize, Serialize};
2use std::{collections::HashSet, io::Write};
3
4#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
5#[serde(from = "StringSequenceDef")]
6#[serde(into = "StringSequenceDef")]
7pub struct StringSequence(String);
8
9impl StringSequence {
10 pub fn new(items: &[String]) -> Self {
11 Self::from_iter(
12 items
13 .iter()
14 .filter(|item| !item.is_empty())
15 .map(|item| item.as_str()),
16 )
17 }
18
19 pub fn append(&mut self, item: &str) {
20 if !item.is_empty() {
21 if self.0.is_empty() {
22 self.0.push_str(item);
23 } else {
24 self.0.reserve(1 + item.len());
25 self.0.push('|');
26 self.0.push_str(item);
27 }
28 }
29 }
30
31 pub fn with(mut self, item: &str) -> Self {
32 self.append(item);
33 self
34 }
35
36 pub fn parts(&self) -> impl Iterator<Item = &str> {
37 self.0.split('|').filter(|chunk| !chunk.is_empty())
38 }
39
40 pub fn rparts(&self) -> impl Iterator<Item = &str> {
41 self.0.rsplit('|').filter(|chunk| !chunk.is_empty())
42 }
43
44 pub fn as_slice(&self) -> StrSequence<'_> {
45 StrSequence(self.0.as_str())
46 }
47}
48
49impl From<&[String]> for StringSequence {
50 fn from(items: &[String]) -> Self {
51 Self::new(items)
52 }
53}
54
55impl<'a> FromIterator<&'a str> for StringSequence {
56 fn from_iter<T>(iter: T) -> Self
57 where
58 T: IntoIterator<Item = &'a str>,
59 {
60 Self(
61 iter.into_iter()
62 .filter(|chunk| !chunk.is_empty())
63 .flat_map(|item| std::iter::once('|').chain(item.chars()))
64 .skip(1)
65 .collect::<String>(),
66 )
67 }
68}
69
70impl From<StringSequenceDef> for StringSequence {
71 fn from(def: StringSequenceDef) -> Self {
72 def.0.iter().map(|item| item.as_str()).collect()
73 }
74}
75
76#[derive(Serialize, Deserialize)]
77#[serde(transparent)]
78struct StringSequenceDef(Vec<String>);
79
80impl From<StringSequence> for StringSequenceDef {
81 fn from(seq: StringSequence) -> Self {
82 Self(seq.parts().map(|item| item.to_owned()).collect())
83 }
84}
85
86#[derive(Debug, PartialEq, Eq, Hash)]
87pub struct StrSequence<'a>(&'a str);
88
89impl<'a> StrSequence<'a> {
90 pub fn parts(&self) -> impl Iterator<Item = &str> {
91 self.0.split('|').filter(|chunk| !chunk.is_empty())
92 }
93
94 pub fn rparts(&self) -> impl Iterator<Item = &str> {
95 self.0.rsplit('|').filter(|chunk| !chunk.is_empty())
96 }
97
98 pub fn to_owned(&self) -> StringSequence {
99 StringSequence(self.0.to_owned())
100 }
101}
102
103#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct TagFilters {
105 #[serde(default)]
106 inclusive: bool,
107 #[serde(default)]
108 tags: HashSet<String>,
109}
110
111impl TagFilters {
112 pub fn inclusive() -> Self {
113 Self {
114 inclusive: true,
115 tags: Default::default(),
116 }
117 }
118
119 pub fn exclusive() -> Self {
120 Self {
121 inclusive: false,
122 tags: Default::default(),
123 }
124 }
125
126 pub fn none() -> Self {
127 Self::inclusive()
128 }
129
130 pub fn all() -> Self {
131 Self::exclusive()
132 }
133
134 pub fn include(mut self, tag: impl ToString) -> Self {
135 if self.inclusive {
136 self.tags.insert(tag.to_string());
137 } else {
138 self.tags.remove(&tag.to_string());
139 }
140 self
141 }
142
143 pub fn include_range(mut self, tags: impl Iterator<Item = impl ToString>) -> Self {
144 for tag in tags {
145 self = self.include(tag.to_string());
146 }
147 self
148 }
149
150 pub fn exclude(mut self, tag: impl ToString) -> Self {
151 if self.inclusive {
152 self.tags.remove(&tag.to_string());
153 } else {
154 self.tags.insert(tag.to_string());
155 }
156 self
157 }
158
159 pub fn exclude_range(mut self, tags: impl Iterator<Item = impl ToString>) -> Self {
160 for tag in tags {
161 self = self.exclude(tag.to_string());
162 }
163 self
164 }
165
166 pub fn combine(mut self, other: &Self) -> Self {
167 if self.inclusive == other.inclusive {
168 self.tags = self.tags.union(&other.tags).cloned().collect();
169 } else {
170 self.tags = self.tags.difference(&other.tags).cloned().collect();
171 }
172 self
173 }
174
175 pub fn validate_tag(&self, tag: &str) -> bool {
176 if self.inclusive {
177 self.tags.contains(tag)
178 } else {
179 !self.tags.contains(tag)
180 }
181 }
182}
183
184#[derive(Clone)]
185pub struct StringBuffer {
186 buffer: Vec<u8>,
187 level: usize,
188 pub indent: usize,
189 pub resize: usize,
190}
191
192impl Default for StringBuffer {
193 fn default() -> Self {
194 Self {
195 buffer: Default::default(),
196 level: 0,
197 indent: 2,
198 resize: 1024,
199 }
200 }
201}
202
203impl StringBuffer {
204 pub fn push_level(&mut self) {
205 self.level += 1;
206 }
207
208 pub fn pop_level(&mut self) {
209 if self.level > 0 {
210 self.level -= 1;
211 }
212 }
213
214 pub fn level(&self) -> usize {
215 self.level
216 }
217
218 pub fn write_indent(&mut self) -> std::io::Result<()> {
219 if self.level > 0 && self.indent > 0 {
220 let count = self.level * self.indent;
221 write!(&mut self.buffer, "{:indent$}", "", indent = count)
222 } else {
223 Ok(())
224 }
225 }
226
227 pub fn write_indented_lines<S>(&mut self, s: S) -> std::io::Result<()>
228 where
229 S: AsRef<str>,
230 {
231 for line in s.as_ref().lines() {
232 if !line.is_empty() {
233 self.write_new_line()?;
234 self.write_str(line.trim())?;
235 }
236 }
237 Ok(())
238 }
239
240 pub fn write_str<S>(&mut self, s: S) -> std::io::Result<()>
241 where
242 S: AsRef<str>,
243 {
244 write!(&mut self.buffer, "{}", s.as_ref())
245 }
246
247 pub fn write_new_line(&mut self) -> std::io::Result<()> {
248 writeln!(&mut self.buffer)?;
249 self.write_indent()
250 }
251
252 pub fn write_space(&mut self) -> std::io::Result<()> {
253 write!(&mut self.buffer, " ")
254 }
255}
256
257impl Write for StringBuffer {
258 fn flush(&mut self) -> std::io::Result<()> {
259 self.buffer.flush()
260 }
261
262 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
263 if self.resize > 0 && self.buffer.len() + buf.len() > self.buffer.capacity() {
264 let count = buf.len() / self.resize + 1;
265 self.buffer.reserve(self.resize * count);
266 }
267 self.buffer.write(buf)
268 }
269}
270
271impl From<StringBuffer> for std::io::Result<String> {
272 fn from(buffer: StringBuffer) -> Self {
273 match String::from_utf8(buffer.buffer) {
274 Ok(result) => Ok(result),
275 Err(error) => Err(std::io::Error::new(std::io::ErrorKind::Other, error)),
276 }
277 }
278}