oxygengine_core/
utils.rs

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}