1use std::{any::type_name, collections::HashMap, hash::Hash, time::Duration};
85
86pub trait EqUtils<T: PartialEq> {
88 fn eq_to(&self, other: &T) -> bool;
90
91 fn not_eq_to(&self, other: &T) -> bool;
93}
94
95impl<T: PartialEq> EqUtils<T> for T {
96 fn eq_to(&self, other: &T) -> bool {
97 self == other
98 }
99 fn not_eq_to(&self, other: &T) -> bool {
100 self != other
101 }
102}
103
104pub trait OptionUtils<T> {
106 fn or_default_with(self, fallback: T) -> T;
108
109 #[must_use]
113 fn if_some<F: FnOnce(&T)>(self, f: F) -> Option<T>;
114}
115
116impl<T> OptionUtils<T> for Option<T> {
117 fn or_default_with(self, fallback: T) -> T {
118 self.unwrap_or(fallback)
119 }
120
121 fn if_some<F: FnOnce(&T)>(self, f: F) -> Option<T> {
122 if let Some(ref val) = self {
123 f(val);
124 }
125 self
126 }
127}
128
129pub trait StrUtils {
131 fn contains_all<'a, I>(&self, parts: I) -> bool
133 where
134 I: IntoIterator<Item = &'a str>;
135
136 fn contains_any<'a, I>(&self, parts: I) -> bool
138 where
139 I: IntoIterator<Item = &'a str>;
140
141 fn to_title_case(&self) -> String;
143}
144
145impl StrUtils for str {
146 fn contains_all<'a, I>(&self, parts: I) -> bool
147 where
148 I: IntoIterator<Item = &'a str>,
149 {
150 parts.into_iter().all(|part| self.contains(part))
151 }
152 fn contains_any<'a, I>(&self, parts: I) -> bool
153 where
154 I: IntoIterator<Item = &'a str>,
155 {
156 parts.into_iter().any(|part| self.contains(part))
157 }
158 fn to_title_case(&self) -> String {
159 let mut chars = self.chars();
160 match chars.next() {
161 None => String::new(),
162 Some(f) => f.to_uppercase().collect::<String>() + chars.as_str(),
163 }
164 }
165}
166
167pub trait MemUtils {
169 fn type_name(&self) -> &'static str;
171
172 fn mem_size(&self) -> usize;
174
175 fn view(&self);
177}
178
179impl<T> MemUtils for T {
180 fn type_name(&self) -> &'static str {
181 type_name::<T>()
182 }
183 fn mem_size(&self) -> usize {
184 std::mem::size_of::<T>()
185 }
186 fn view(&self) {
187 println!(
188 "[view] Type: {}, Size: {} bytes",
189 self.type_name(),
190 self.mem_size()
191 );
192 }
193}
194
195pub trait ConvertUtils: Sized {
196 fn to<T: TryFrom<Self>>(self) -> Option<T>;
197 fn to_or<T: TryFrom<Self>>(self, fallback: T) -> T;
198 fn to_result<T: TryFrom<Self>>(self) -> Result<T, T::Error>;
199}
200
201impl<T> ConvertUtils for T {
202 fn to<U: TryFrom<T>>(self) -> Option<U> {
203 U::try_from(self).ok()
204 }
205
206 fn to_or<U: TryFrom<T>>(self, fallback: U) -> U {
207 self.to().unwrap_or(fallback)
208 }
209
210 fn to_result<U: TryFrom<T>>(self) -> Result<U, U::Error> {
211 U::try_from(self)
212 }
213}
214
215pub trait BoolUtils {
216 #[must_use]
217 fn not(&self) -> bool;
218 fn then_val<T>(&self, val: T) -> Option<T>;
219 fn if_true<T, F: FnOnce() -> T>(&self, f: F) -> Option<T>;
220 fn if_false<T, F: FnOnce() -> T>(&self, f: F) -> Option<T>;
221 fn toggle(&mut self);
222}
223
224impl BoolUtils for bool {
225 fn not(&self) -> bool {
226 !self
227 }
228 fn then_val<T>(&self, val: T) -> Option<T> {
229 if *self { Some(val) } else { None }
230 }
231 fn if_true<T, F: FnOnce() -> T>(&self, f: F) -> Option<T> {
232 if *self { Some(f()) } else { None }
233 }
234 fn if_false<T, F: FnOnce() -> T>(&self, f: F) -> Option<T> {
235 if self.not() { Some(f()) } else { None }
236 }
237 fn toggle(&mut self) {
238 *self = !*self;
239 }
240}
241
242pub trait VecUtils<T> {
244 fn push_if(&mut self, push: T, cond: bool);
246
247 fn push_if_with<F: FnOnce() -> T>(&mut self, cond: bool, f: F);
249}
250
251impl<T> VecUtils<T> for Vec<T> {
252 fn push_if(&mut self, push: T, cond: bool) {
253 if cond {
254 self.push(push);
255 }
256 }
257 fn push_if_with<F: FnOnce() -> T>(&mut self, cond: bool, f: F) {
258 if cond {
259 self.push(f());
260 }
261 }
262}
263
264pub trait MapUtils<K, V> {
265 fn get_or<'a>(&'a self, key: &K, fallback: &'a V) -> &'a V;
266 fn insert_if(&mut self, key: K, value: V, cond: bool);
267}
268
269impl<K: Eq + Hash, V> MapUtils<K, V> for HashMap<K, V> {
270 fn get_or<'a>(&'a self, key: &K, fallback: &'a V) -> &'a V {
271 self.get(key).unwrap_or(fallback)
272 }
273
274 fn insert_if(&mut self, key: K, value: V, cond: bool) {
275 if cond {
276 self.insert(key, value);
277 }
278 }
279}
280
281pub trait ResultUtils<T, E> {
282 fn if_ok<F: FnOnce(&T)>(self, f: F) -> Self;
283 fn if_err<F: FnOnce(&E)>(self, f: F) -> Self;
284}
285
286impl<T, E: std::fmt::Debug> ResultUtils<T, E> for Result<T, E> {
287 fn if_ok<F: FnOnce(&T)>(self, f: F) -> Self {
288 if let Ok(ref val) = self {
289 f(val);
290 }
291 self
292 }
293
294 fn if_err<F: FnOnce(&E)>(self, f: F) -> Self {
295 if let Err(ref err) = self {
296 f(err);
297 }
298 self
299 }
300}
301
302pub trait DurationUtils {
304 fn pretty(&self) -> String;
306}
307
308impl DurationUtils for Duration {
309 fn pretty(&self) -> String {
310 let total_secs = self.as_secs();
311 let hours = total_secs / 3600;
312 let mins = (total_secs % 3600) / 60;
313 let secs = total_secs % 60;
314 format!("{}h {}m {}s", hours, mins, secs)
315 }
316}
317
318pub trait IteratorUtils: Iterator + Sized {
319 fn find_map_or<T, F: FnMut(Self::Item) -> Option<T>>(self, f: F, fallback: T) -> T;
320}
321
322impl<I: Iterator> IteratorUtils for I {
323 fn find_map_or<T, F: FnMut(Self::Item) -> Option<T>>(mut self, f: F, fallback: T) -> T {
324 self.find_map(f).unwrap_or(fallback)
325 }
326}
327
328pub trait IdentityUtils: Sized {
329 fn tap<F: FnOnce(&Self)>(self, f: F) -> Self;
330}
331
332impl<T> IdentityUtils for T {
333 fn tap<F: FnOnce(&Self)>(self, f: F) -> Self {
334 f(&self);
335 self
336 }
337}
338
339pub trait PanicUtils<T> {
341 fn unwrap_or_exit(self, msg: &str) -> T;
343}
344
345impl<T> PanicUtils<T> for Option<T> {
346 fn unwrap_or_exit(self, msg: &str) -> T {
347 self.unwrap_or_else(|| {
348 eprintln!("[FATAL]: {}", msg);
349 std::process::exit(1);
350 })
351 }
352}
353
354impl<T, U> PanicUtils<T> for Result<T, U> {
355 fn unwrap_or_exit(self, msg: &str) -> T {
356 self.unwrap_or_else(|_| {
357 eprintln!("[FATAL]: {}", msg);
358 std::process::exit(1);
359 })
360 }
361}
362
363pub trait ClampUtils {
364 fn clamp_to(self, min: Self, max: Self) -> Self;
365}
366impl ClampUtils for i32 {
367 fn clamp_to(self, min: Self, max: Self) -> Self {
368 self.max(min).min(max)
369 }
370}
371
372pub trait NumberUtils {
373 #[must_use]
374 fn is_even(&self) -> bool;
375 #[must_use]
376 fn is_odd(&self) -> bool;
377}
378
379impl NumberUtils for i32 {
380 fn is_even(&self) -> bool {
381 self % 2 == 0
382 }
383 fn is_odd(&self) -> bool {
384 self % 2 != 0
385 }
386}