Skip to main content

mkutils/
utils.rs

1#[cfg(feature = "serde")]
2use crate::as_valuable::AsValuable;
3#[cfg(feature = "fmt")]
4use crate::fmt::{Debugged, OptionalDisplay};
5#[cfg(feature = "tui")]
6use crate::geometry::Orientation;
7#[cfg(feature = "ropey")]
8use crate::geometry::PointUsize;
9use crate::is::Is;
10#[cfg(feature = "output")]
11use crate::output::Output;
12#[cfg(feature = "process")]
13use crate::process::ProcessBuilder;
14#[cfg(feature = "ropey")]
15use crate::rope_builder::RopeBuilder;
16#[cfg(feature = "tui")]
17use crate::scrollable::{ScrollCount, ScrollWhen, Scrollable};
18#[cfg(any(feature = "serde", feature = "tui"))]
19use crate::seq_visitor::SeqVisitor;
20#[cfg(feature = "socket")]
21use crate::socket::{Request, Socket};
22#[cfg(feature = "tracing")]
23use crate::status::Status;
24#[cfg(any(feature = "ropey", feature = "tui"))]
25use crate::transpose::Transpose;
26#[cfg(feature = "async")]
27use crate::{into_stream::IntoStream, read_value::ReadValue, run_for::RunForError};
28#[cfg(any(
29    feature = "async",
30    feature = "fs",
31    feature = "process",
32    feature = "reqwest",
33    feature = "socket",
34    feature = "tui",
35))]
36use anyhow::{Context, Error as AnyhowError};
37#[cfg(feature = "async")]
38use bytes::Buf;
39#[cfg(feature = "poem")]
40use bytes::Bytes;
41#[cfg(feature = "fs")]
42use camino::{Utf8Path, Utf8PathBuf};
43#[cfg(any(feature = "async", feature = "poem"))]
44use futures::Stream;
45#[cfg(feature = "async")]
46use futures::{Sink, SinkExt, StreamExt, TryFuture, future::Either, stream::Filter, stream::FuturesUnordered};
47#[cfg(feature = "tui")]
48use num::traits::{SaturatingAdd, SaturatingSub};
49#[cfg(any(feature = "ropey", feature = "misc", feature = "tui"))]
50use num::{Bounded, NumCast, ToPrimitive, Zero};
51#[cfg(feature = "poem")]
52use poem::{Body as PoemBody, Endpoint, IntoResponse, web::websocket::Message as PoemMessage};
53#[cfg(feature = "poem")]
54use poem_openapi::{error::ParseRequestPayloadError, payload::Binary as PoemBinary, payload::Json as PoemJson};
55#[cfg(feature = "tui")]
56use ratatui::{
57    Frame,
58    layout::Rect,
59    text::{Line, Span},
60    widgets::{Block, Widget},
61};
62#[cfg(feature = "reqwest")]
63use reqwest::{RequestBuilder, Response};
64#[cfg(feature = "rmp")]
65use rmp_serde::{decode::Error as RmpDecodeError, encode::Error as RmpEncodeError};
66#[cfg(feature = "ropey")]
67use ropey::{Rope, RopeSlice, iter::Chunks, iter::Lines};
68#[cfg(any(feature = "rmp", feature = "serde", feature = "tui"))]
69use serde::Deserialize;
70#[cfg(any(feature = "serde", feature = "tui"))]
71use serde::Deserializer;
72#[cfg(any(feature = "reqwest", feature = "rmp", feature = "serde"))]
73use serde::Serialize;
74#[cfg(feature = "serde")]
75use serde::de::DeserializeOwned;
76#[cfg(any(feature = "poem", feature = "serde"))]
77use serde_json::Error as SerdeJsonError;
78#[cfg(feature = "serde")]
79use serde_json::{Value as Json, value::Index};
80#[cfg(feature = "serde")]
81use serde_yaml_ng::Error as SerdeYamlError;
82#[cfg(any(
83    feature = "async",
84    feature = "fs",
85    feature = "process",
86    feature = "reqwest",
87    feature = "socket",
88    feature = "tui",
89))]
90use std::fmt::Debug;
91#[cfg(feature = "fs")]
92use std::path::PathBuf;
93#[cfg(feature = "process")]
94use std::process::ExitStatus;
95use std::{
96    borrow::{Borrow, BorrowMut, Cow},
97    collections::HashMap,
98    error::Error as StdError,
99    fmt::Display,
100    fs::File,
101    future::Ready,
102    hash::Hash,
103    io::{BufReader, BufWriter, Error as IoError, Read, Write},
104    iter::{Once, Repeat},
105    mem::ManuallyDrop,
106    ops::{Add, ControlFlow, Range},
107    path::Path,
108    pin::Pin,
109    str::Utf8Error,
110    sync::{
111        Arc,
112        atomic::{AtomicUsize, Ordering},
113    },
114    task::Poll,
115    time::Instant,
116};
117#[cfg(feature = "async")]
118use std::{fs::Metadata, time::Duration};
119#[cfg(any(feature = "async", feature = "process"))]
120use tokio::io::{AsyncReadExt, AsyncWriteExt};
121#[cfg(any(feature = "async", feature = "ropey"))]
122use tokio::{
123    fs::File as TokioFile,
124    io::{AsyncRead, BufReader as TokioBufReader},
125};
126#[cfg(feature = "async")]
127use tokio::{
128    io::{AsyncWrite, BufWriter as TokioBufWriter},
129    sync::oneshot::Sender as OneshotSender,
130    task::JoinHandle,
131    task::{JoinSet, LocalSet},
132    time::{Interval, Sleep, Timeout},
133};
134#[cfg(feature = "async")]
135use tokio_util::{
136    codec::{Framed, LengthDelimitedCodec, LinesCodec},
137    io::StreamReader,
138};
139#[cfg(feature = "tracing")]
140use tracing::Level;
141#[cfg(any(feature = "ropey", feature = "tui"))]
142use unicode_segmentation::{GraphemeIndices, Graphemes, UnicodeSegmentation};
143#[cfg(feature = "serde")]
144use valuable::Value;
145
146#[allow(async_fn_in_trait)]
147pub trait Utils {
148    const DEFAULT_ROPE_BUILDER_BUFFER_SIZE: usize = 8192;
149    const IS_EXTENDED: bool = true;
150    const NEWLINE: &str = "\n";
151    const READ_FROM_CLIPBOARD_COMMAND: &str = "pbpaste";
152    const WRITE_TO_CLIPBOARD_COMMAND: &str = "pbcopy";
153
154    #[cfg(feature = "async")]
155    async fn abort_all_and_wait<T: 'static>(&mut self)
156    where
157        Self: BorrowMut<JoinSet<T>>,
158    {
159        let join_set = self.borrow_mut();
160
161        join_set.abort_all();
162
163        while join_set.join_next().await.is_some() {}
164    }
165
166    #[cfg(feature = "fs")]
167    fn absolute_utf8(&self) -> Result<Cow<'_, Utf8Path>, IoError>
168    where
169        Self: AsRef<Utf8Path>,
170    {
171        let path = self.as_ref();
172
173        if path.is_absolute() {
174            path.borrowed().ok()
175        } else {
176            camino::absolute_utf8(path)?.owned::<Utf8Path>().ok()
177        }
178    }
179
180    async fn achain<T: Future>(self, rhs: T) -> T::Output
181    where
182        Self: Future + Sized,
183    {
184        self.await;
185
186        rhs.await
187    }
188
189    #[cfg(feature = "tui")]
190    fn add_span<'a, T: Into<Span<'a>>>(self, span: T) -> Line<'a>
191    where
192        Self: Into<Line<'a>>,
193    {
194        let mut line = self.into();
195
196        line.spans.push(span.into());
197
198        line
199    }
200
201    #[cfg(any(
202        feature = "async",
203        feature = "fs",
204        feature = "process",
205        feature = "reqwest",
206        feature = "socket",
207        feature = "tui",
208    ))]
209    fn anyhow_msg_error(self) -> AnyhowError
210    where
211        Self: 'static + Debug + Display + Send + Sized + Sync,
212    {
213        AnyhowError::msg(self)
214    }
215
216    #[cfg(any(
217        feature = "async",
218        feature = "fs",
219        feature = "process",
220        feature = "reqwest",
221        feature = "socket",
222        feature = "tui",
223    ))]
224    fn anyhow_error(self) -> AnyhowError
225    where
226        Self: Into<AnyhowError>,
227    {
228        self.into()
229    }
230
231    #[cfg(any(
232        feature = "async",
233        feature = "fs",
234        feature = "process",
235        feature = "reqwest",
236        feature = "socket",
237        feature = "tui",
238    ))]
239    fn anyhow_result<T, E: Into<AnyhowError>>(self) -> Result<T, AnyhowError>
240    where
241        Self: Is<Result<T, E>>,
242    {
243        self.into_self().map_err(E::into)
244    }
245
246    fn arc(self) -> Arc<Self>
247    where
248        Self: Sized,
249    {
250        Arc::new(self)
251    }
252
253    async fn async_with<T>(self, next: impl Future<Output = T>) -> T
254    where
255        Self: Future + Sized,
256    {
257        self.await;
258
259        next.await
260    }
261
262    fn as_borrowed<'a, B: ?Sized + ToOwned>(&'a self) -> &'a B
263    where
264        Self: Borrow<Cow<'a, B>>,
265    {
266        self.borrow().borrow()
267    }
268
269    fn as_immut(&mut self) -> &Self {
270        &*self
271    }
272
273    fn as_ptr(&self) -> *const Self {
274        std::ptr::from_ref(self)
275    }
276
277    fn as_ptr_mut(&mut self) -> *mut Self {
278        std::ptr::from_mut(self)
279    }
280
281    fn as_utf8(&self) -> Result<&str, Utf8Error>
282    where
283        Self: AsRef<[u8]>,
284    {
285        std::str::from_utf8(self.as_ref())
286    }
287
288    #[cfg(feature = "fs")]
289    fn as_utf8_path(&self) -> &Utf8Path
290    where
291        Self: AsRef<Utf8Path>,
292    {
293        self.as_ref()
294    }
295
296    #[cfg(feature = "ropey")]
297    fn as_slice(&self) -> RopeSlice<'_>
298    where
299        Self: Borrow<Rope>,
300    {
301        self.borrow().slice(..)
302    }
303
304    #[cfg(feature = "serde")]
305    fn as_valuable(&self) -> Value<'_>
306    where
307        Self: AsValuable,
308    {
309        AsValuable::as_valuable(self)
310    }
311
312    #[cfg(feature = "tui")]
313    fn bordered_block<'a>(self) -> Block<'a>
314    where
315        Self: Into<Line<'a>> + Sized,
316    {
317        Block::bordered().title(self)
318    }
319
320    fn borrowed(&self) -> Cow<'_, Self>
321    where
322        Self: ToOwned,
323    {
324        Cow::Borrowed(self)
325    }
326
327    fn buf_reader(self) -> BufReader<Self>
328    where
329        Self: Read + Sized,
330    {
331        BufReader::new(self)
332    }
333
334    #[cfg(any(feature = "async", feature = "ropey"))]
335    fn buf_reader_async(self) -> TokioBufReader<Self>
336    where
337        Self: AsyncRead + Sized,
338    {
339        TokioBufReader::new(self)
340    }
341
342    fn buf_writer(self) -> BufWriter<Self>
343    where
344        Self: Write + Sized,
345    {
346        BufWriter::new(self)
347    }
348
349    #[cfg(feature = "async")]
350    fn buf_writer_async(self) -> TokioBufWriter<Self>
351    where
352        Self: AsyncWrite + Sized,
353    {
354        TokioBufWriter::new(self)
355    }
356
357    #[cfg(any(feature = "ropey", feature = "misc", feature = "tui"))]
358    fn cast_or_max<T: Bounded + NumCast>(self) -> T
359    where
360        Self: Sized + ToPrimitive,
361    {
362        match T::from(self) {
363            Some(value) => value,
364            None => T::max_value(),
365        }
366    }
367
368    // NOTE: requires that [Self] and [T] have the same layout (provided by [#[repr(transparent)]]):
369    // [https://stackoverflow.com/questions/79593399/implement-valuablevaluable-on-serde-jsonvalue]
370    fn cast_ref<T>(&self) -> &T {
371        let ptr = std::ptr::from_ref(self).cast::<T>();
372        let value = unsafe { ptr.as_ref() };
373
374        value.unwrap()
375    }
376
377    fn cat<T: Display>(&self, rhs: T) -> String
378    where
379        Self: Display,
380    {
381        std::format!("{self}{rhs}")
382    }
383
384    #[cfg(any(
385        feature = "async",
386        feature = "fs",
387        feature = "process",
388        feature = "reqwest",
389        feature = "socket",
390        feature = "tui",
391    ))]
392    fn check_next<T>(self) -> Result<T, AnyhowError>
393    where
394        Self: Is<Option<T>>,
395    {
396        match self.into_self() {
397            Some(item) => item.ok(),
398            None => anyhow::bail!(
399                "sequence of {type_name} items is exhausted",
400                type_name = Self::type_name(),
401            ),
402        }
403    }
404
405    #[cfg(any(
406        feature = "async",
407        feature = "fs",
408        feature = "process",
409        feature = "reqwest",
410        feature = "socket",
411        feature = "tui",
412    ))]
413    fn check_present<T>(self) -> Result<T, AnyhowError>
414    where
415        Self: Is<Option<T>>,
416    {
417        match self.into_self() {
418            Some(item) => item.ok(),
419            None => anyhow::bail!(
420                "keyed entry of type {type_name} is not present in the collection",
421                type_name = Self::type_name(),
422            ),
423        }
424    }
425
426    // NOTE: [https://docs.rs/reqwest/latest/reqwest/struct.Response.html#method.error_for_status]
427    #[cfg(feature = "reqwest")]
428    async fn check_status(self) -> Result<Response, AnyhowError>
429    where
430        Self: Is<Response>,
431    {
432        let response = self.into_self();
433        let status = response.status();
434
435        if !status.is_client_error() && !status.is_server_error() {
436            return response.ok();
437        }
438
439        let text = match response.text().await {
440            Ok(text) => text,
441            Err(error) => std::format!("unable to read response text: {error}"),
442        };
443
444        anyhow::bail!("({status}) {text}")
445    }
446
447    #[cfg(any(feature = "ropey", feature = "misc", feature = "tui"))]
448    #[must_use]
449    fn clamped(self, min: Self, max: Self) -> Self
450    where
451        Self: PartialOrd + Sized,
452    {
453        num::clamp(self, min, max)
454    }
455
456    fn convert<T: From<Self>>(self) -> T
457    where
458        Self: Sized,
459    {
460        self.into()
461    }
462
463    fn contains_eq<Q, K>(&self, query: Q) -> bool
464    where
465        Self: AsRef<[K]>,
466        for<'a> &'a K: PartialEq<Q>,
467    {
468        self.find_eq(query).is_some()
469    }
470
471    #[cfg(any(
472        feature = "async",
473        feature = "fs",
474        feature = "process",
475        feature = "reqwest",
476        feature = "socket",
477        feature = "tui",
478    ))]
479    fn context_path<T, E, C: 'static + Display + Send + Sync, P: AsRef<Path>>(
480        self,
481        context: C,
482        path: P,
483    ) -> Result<T, AnyhowError>
484    where
485        Self: Context<T, E> + Sized,
486    {
487        let context = std::format!("{context}: {path}", path = path.as_ref().display());
488
489        self.context(context)
490    }
491
492    #[must_use]
493    fn copied(&self) -> Self
494    where
495        Self: Copy + Sized,
496    {
497        *self
498    }
499
500    #[cfg(feature = "process")]
501    async fn write_to_clipboard(&self) -> Result<ExitStatus, AnyhowError>
502    where
503        Self: AsRef<[u8]>,
504    {
505        let mut process = ProcessBuilder::new(Self::WRITE_TO_CLIPBOARD_COMMAND).build()?;
506
507        process
508            .stdin_mut()
509            .write_all_then_async(self.as_ref())
510            .await?
511            .flush()
512            .await?;
513
514        process.run().await?.ok()
515    }
516
517    #[cfg(feature = "process")]
518    #[must_use]
519    async fn read_from_clipboard() -> Result<String, AnyhowError> {
520        let mut process = ProcessBuilder::new(Self::READ_FROM_CLIPBOARD_COMMAND).build()?;
521        let string = process.stdout_mut().read_to_string_async().await?;
522
523        process.run().await?;
524
525        string.ok()
526    }
527
528    fn create(&self) -> Result<File, IoError>
529    where
530        Self: AsRef<Path>,
531    {
532        File::create(self)
533    }
534
535    fn create_dir_all(&self) -> Result<(), IoError>
536    where
537        Self: AsRef<Path>,
538    {
539        std::fs::create_dir_all(self)
540    }
541
542    #[cfg(feature = "misc")]
543    fn cycle_in_place(&mut self, amount: isize, total: usize)
544    where
545        Self: BorrowMut<usize>,
546    {
547        let current = self.borrow_mut();
548
549        // NOTE: [isize::rem_euclid()] returns a nonnegative integer
550        // [https://doc.rust-lang.org/stable/std/primitive.isize.html#method.rem_euclid] so casting as a [usize] is
551        // fine
552        *current = amount
553            .saturating_add_unsigned(*current)
554            .rem_euclid(total.cast_or_max())
555            .cast_or_max();
556    }
557
558    #[cfg(feature = "fmt")]
559    fn debug(&self) -> Debugged<'_, Self> {
560        Debugged::new(self)
561    }
562
563    #[cfg(any(feature = "serde", feature = "tui"))]
564    fn deserialize_from_seq<'de, D: Deserializer<'de>, X: Deserialize<'de>, Y, E: Display, F: Fn(X) -> Result<Y, E>>(
565        deserializer: D,
566        func: F,
567    ) -> Result<Self, D::Error>
568    where
569        Self: Default + Extend<Y>,
570    {
571        let seq_visitor = SeqVisitor::new(func);
572
573        deserializer.deserialize_seq(seq_visitor)
574    }
575
576    #[cfg(feature = "output")]
577    fn end_err<T>(self) -> Output<T, Self>
578    where
579        Self: Sized,
580    {
581        Output::EndErr(self)
582    }
583
584    #[cfg(feature = "output")]
585    fn end_ok<T, E>(&self) -> Output<T, E> {
586        Output::EndOk
587    }
588
589    fn err<T>(self) -> Result<T, Self>
590    where
591        Self: Sized,
592    {
593        Err(self)
594    }
595
596    #[cfg(feature = "fs")]
597    fn expand_user(&self) -> Cow<'_, Utf8Path>
598    where
599        Self: AsRef<str>,
600    {
601        let path_str = self.as_ref();
602
603        if let Some(relative_path_str) = path_str.strip_prefix("~/")
604            && let Some(home_dirpath) = Self::home_dirpath()
605        {
606            home_dirpath.join(relative_path_str).owned()
607        } else {
608            path_str.as_utf8_path().borrowed()
609        }
610    }
611
612    #[cfg(feature = "fs")]
613    fn unexpand_user(&self) -> Cow<'_, Utf8Path>
614    where
615        Self: AsRef<str>,
616    {
617        let path_str = self.as_ref();
618
619        if let Some(home_dirpath) = Self::home_dirpath()
620            && let Some(relative_path_str) = path_str.strip_prefix(home_dirpath.as_str())
621        {
622            if relative_path_str.is_empty() {
623                "~".convert::<Utf8PathBuf>().owned()
624            } else if relative_path_str.as_utf8_path().is_absolute() {
625                "~".cat(relative_path_str).convert::<Utf8PathBuf>().owned()
626            } else {
627                path_str.as_utf8_path().borrowed()
628            }
629        } else {
630            path_str.as_utf8_path().borrowed()
631        }
632    }
633
634    #[cfg(any(feature = "ropey", feature = "tui"))]
635    fn extended_graphemes(&self) -> Graphemes<'_>
636    where
637        Self: AsRef<str>,
638    {
639        self.as_ref().graphemes(Self::IS_EXTENDED)
640    }
641
642    #[cfg(any(feature = "ropey", feature = "tui"))]
643    fn extended_grapheme_indices(&self) -> GraphemeIndices<'_>
644    where
645        Self: AsRef<str>,
646    {
647        self.as_ref().grapheme_indices(Self::IS_EXTENDED)
648    }
649
650    #[cfg(feature = "ropey")]
651    fn extended_graphemes_at<'a>(self, extended_graphemes_index_range: Range<usize>) -> impl Iterator<Item = &'a str>
652    where
653        Self: Is<RopeSlice<'a>>,
654    {
655        let extended_graphemes_index_range = extended_graphemes_index_range.borrow();
656
657        self.into_self()
658            .chunks()
659            .flat_map(str::extended_graphemes)
660            .skip(extended_graphemes_index_range.start)
661            .take(extended_graphemes_index_range.len())
662    }
663
664    #[cfg(feature = "ropey")]
665    fn extended_graphemes_at_rect<'a>(
666        self,
667        lines_index_range: Range<usize>,
668        extended_graphemes_index_range: Range<usize>,
669    ) -> impl Iterator<Item = impl Iterator<Item = &'a str>>
670    where
671        Self: Is<RopeSlice<'a>>,
672    {
673        self.into_self()
674            .saturating_lines_at(lines_index_range.start)
675            .take(lines_index_range.len())
676            .map(move |line_rope_slice| line_rope_slice.extended_graphemes_at(extended_graphemes_index_range.clone()))
677    }
678
679    #[cfg(any(feature = "ropey", feature = "tui"))]
680    fn extended_grapheme_substring(&self, range: Range<usize>) -> &str
681    where
682        Self: AsRef<str>,
683    {
684        let string = self.as_ref();
685        let mut extended_grapheme_indices = string.extended_grapheme_indices().skip(range.start);
686        let Some((begin_byte_offset, _begin_extended_grapheme)) = extended_grapheme_indices.next() else {
687            return "";
688        };
689        let mut extended_grapheme_indices = extended_grapheme_indices.skip(range.len());
690        let end_byte_offset = if let Some((end_byte_offset, _end_extended_grapheme)) = extended_grapheme_indices.next()
691        {
692            end_byte_offset
693        } else {
694            string.len()
695        };
696
697        &string[begin_byte_offset..end_byte_offset]
698    }
699
700    #[cfg(feature = "async")]
701    fn filter_sync(
702        self,
703        mut func: impl FnMut(&Self::Item) -> bool,
704    ) -> Filter<Self, Ready<bool>, impl FnMut(&Self::Item) -> Ready<bool>>
705    where
706        Self: Sized + StreamExt,
707    {
708        // TODO: figure out why [func.pipe(bool::ready)] won't work
709        self.filter(move |x| func(x).ready())
710    }
711
712    #[cfg(feature = "fs")]
713    fn file_name_ok(&self) -> Result<&str, AnyhowError>
714    where
715        Self: AsRef<Utf8Path>,
716    {
717        self.as_ref().file_name().context("path has no file name")
718    }
719
720    fn find_eq<Q, K>(&self, query: Q) -> Option<(usize, &K)>
721    where
722        Self: AsRef<[K]>,
723        for<'a> &'a K: PartialEq<Q>,
724    {
725        self.as_ref().iter().enumerate().find(|(_index, key)| *key == query)
726    }
727
728    fn has_happened(self) -> bool
729    where
730        Self: Is<Instant>,
731    {
732        self.into_self() <= Instant::now()
733    }
734
735    #[cfg(feature = "fs")]
736    #[must_use]
737    fn home_dirpath() -> Option<Utf8PathBuf> {
738        home::home_dir()?.try_convert::<Utf8PathBuf>().ok()
739    }
740
741    fn if_else<T>(self, true_value: T, false_value: T) -> T
742    where
743        Self: Is<bool>,
744    {
745        if self.into_self() { true_value } else { false_value }
746    }
747
748    fn immutable(&mut self) -> &Self {
749        self
750    }
751
752    fn inc(&self) -> usize
753    where
754        Self: Borrow<AtomicUsize>,
755    {
756        self.borrow().fetch_add(1, Ordering::SeqCst)
757    }
758
759    fn insert_mut<'a, K: 'a + Eq + Hash, V>(&'a mut self, key: K, value: V) -> &'a mut V
760    where
761        Self: BorrowMut<HashMap<K, V>>,
762    {
763        self.borrow_mut().entry(key).insert_entry(value).into_mut()
764    }
765
766    #[cfg(any(feature = "ropey", feature = "misc", feature = "tui"))]
767    #[must_use]
768    fn interpolate(
769        self,
770        old_min: impl ToPrimitive,
771        old_max: impl ToPrimitive,
772        new_min: impl ToPrimitive,
773        new_max: impl ToPrimitive,
774    ) -> Self
775    where
776        Self: Bounded + NumCast + ToPrimitive,
777    {
778        let old_min = old_min.cast_or_max::<f64>();
779        let old_max = old_max.cast_or_max::<f64>();
780        let new_min = new_min.cast_or_max::<f64>();
781        let new_max = new_max.cast_or_max::<f64>();
782        let old_value = self.cast_or_max::<f64>().clamped(old_min, old_max);
783        let new_value = new_min + (new_max - new_min) * (old_value - old_min) / (old_max - old_min);
784        let new_value = new_value.clamped(new_min, new_max);
785
786        new_value.cast_or_max()
787    }
788
789    fn into_box(self) -> Box<Self>
790    where
791        Self: Sized,
792    {
793        Box::new(self)
794    }
795
796    fn into_break<C>(self) -> ControlFlow<Self, C>
797    where
798        Self: Sized,
799    {
800        ControlFlow::Break(self)
801    }
802
803    fn into_continue<B>(self) -> ControlFlow<B, Self>
804    where
805        Self: Sized,
806    {
807        ControlFlow::Continue(self)
808    }
809
810    #[cfg(feature = "poem")]
811    fn into_endpoint(self) -> impl Endpoint<Output = Self>
812    where
813        Self: Clone + IntoResponse + Sync,
814    {
815        let func = move |_request| self.clone();
816
817        poem::endpoint::make_sync(func)
818    }
819
820    #[cfg(feature = "async")]
821    fn into_interval(self) -> Interval
822    where
823        Self: Is<Duration>,
824    {
825        tokio::time::interval(self.into_self())
826    }
827
828    #[cfg(feature = "async")]
829    fn into_left<R>(self) -> Either<Self, R>
830    where
831        Self: Sized,
832    {
833        Either::Left(self)
834    }
835
836    #[cfg(feature = "tui")]
837    fn into_line<'a>(self) -> Line<'a>
838    where
839        Self: Into<Cow<'a, str>>,
840    {
841        self.into().into()
842    }
843
844    fn into_manually_drop(self) -> ManuallyDrop<Self>
845    where
846        Self: Sized,
847    {
848        ManuallyDrop::new(self)
849    }
850
851    #[cfg(feature = "async")]
852    fn into_stream_reader<B: Buf, E: Into<IoError>>(self) -> StreamReader<Self, B>
853    where
854        Self: Sized + Stream<Item = Result<B, E>>,
855    {
856        StreamReader::new(self)
857    }
858
859    #[cfg(feature = "async")]
860    fn into_length_delimited_frames(self) -> Framed<Self, LengthDelimitedCodec>
861    where
862        Self: Sized,
863    {
864        Framed::new(self, LengthDelimitedCodec::new())
865    }
866
867    #[cfg(feature = "async")]
868    fn into_line_frames(self) -> Framed<Self, LinesCodec>
869    where
870        Self: Sized,
871    {
872        Framed::new(self, LinesCodec::new())
873    }
874
875    #[cfg(feature = "async")]
876    fn into_right<L>(self) -> Either<L, Self>
877    where
878        Self: Sized,
879    {
880        Either::Right(self)
881    }
882
883    // NOTE: [https://docs.rs/poem-openapi/latest/src/poem_openapi/payload/json.rs.html]
884    #[cfg(feature = "poem")]
885    fn into_parse_request_payload_result<T>(self) -> Result<T, ParseRequestPayloadError>
886    where
887        Self: Is<Result<T, SerdeJsonError>>,
888    {
889        match self.into_self() {
890            Ok(value) => value.ok(),
891            Err(serde_json_error) => ParseRequestPayloadError {
892                reason: serde_json_error.to_string(),
893            }
894            .err(),
895        }
896    }
897
898    #[cfg(feature = "async")]
899    async fn into_select<T: Future>(self, rhs: T) -> Either<Self::Output, T::Output>
900    where
901        Self: Future + Sized,
902    {
903        tokio::select! {
904            value = self => value.into_left(),
905            value = rhs => value.into_right(),
906        }
907    }
908
909    #[cfg(feature = "tracing")]
910    fn into_status<T, E>(self) -> Status<T, E>
911    where
912        Self: Is<Result<T, E>>,
913    {
914        Status::new(self.into_self())
915    }
916
917    #[cfg(feature = "async")]
918    fn into_stream(self) -> Self::Stream
919    where
920        Self: IntoStream + Sized,
921    {
922        IntoStream::into_stream(self)
923    }
924
925    #[cfg(feature = "fs")]
926    fn into_string(self) -> Result<String, AnyhowError>
927    where
928        Self: Is<PathBuf>,
929    {
930        match self.into_self().into_os_string().into_string() {
931            Ok(string) => string.ok(),
932            Err(os_string) => os_string.invalid_utf8_err(),
933        }
934    }
935
936    #[cfg(feature = "serde")]
937    fn into_value_from_json<T: DeserializeOwned>(self) -> Result<T, SerdeJsonError>
938    where
939        Self: Is<Json>,
940    {
941        serde_json::from_value(self.into_self())
942    }
943
944    #[cfg(feature = "fs")]
945    fn invalid_utf8_err<T>(&self) -> Result<T, AnyhowError>
946    where
947        Self: Debug,
948    {
949        anyhow::bail!("{self:?} is not valid utf-8")
950    }
951
952    fn io_error(self) -> IoError
953    where
954        Self: Into<Box<dyn StdError + Send + Sync>>,
955    {
956        IoError::other(self)
957    }
958
959    fn io_result<T, E: Into<Box<dyn StdError + Send + Sync>>>(self) -> Result<T, IoError>
960    where
961        Self: Is<Result<T, E>>,
962    {
963        self.into_self().map_err(E::io_error)
964    }
965
966    #[allow(clippy::wrong_self_convention)]
967    fn is_less_than<T: PartialOrd>(self, rhs: T) -> bool
968    where
969        Self: Into<T>,
970    {
971        self.into() < rhs
972    }
973
974    #[cfg(any(feature = "ropey", feature = "misc", feature = "tui"))]
975    fn is_positive(&self) -> bool
976    where
977        Self: PartialOrd + Zero,
978    {
979        &Self::zero() < self
980    }
981
982    #[cfg(feature = "async")]
983    async fn join_all<T>(self) -> T
984    where
985        Self: IntoIterator<Item: Future> + Sized,
986        T: FromIterator<<Self::Item as Future>::Output>,
987    {
988        futures::future::join_all(self).await.into_iter().collect()
989    }
990
991    #[cfg(any(feature = "ropey", feature = "tui"))]
992    fn len_extended_graphemes(&self) -> usize
993    where
994        Self: AsRef<str>,
995    {
996        self.as_ref().extended_graphemes().count()
997    }
998
999    #[cfg(feature = "tracing")]
1000    fn level<T, E>(&self) -> Level
1001    where
1002        Self: Borrow<Result<T, E>>,
1003    {
1004        if self.borrow().is_ok() {
1005            Level::INFO
1006        } else {
1007            Level::WARN
1008        }
1009    }
1010
1011    #[cfg(any(feature = "tui", feature = "tracing"))]
1012    fn log_error(&self)
1013    where
1014        Self: Display,
1015    {
1016        tracing::warn!(error = %self, "error: {self:#}");
1017    }
1018
1019    #[cfg(any(feature = "tui", feature = "tracing"))]
1020    #[must_use]
1021    fn log_if_error<T, E: Display>(self) -> Self
1022    where
1023        Self: Borrow<Result<T, E>> + Sized,
1024    {
1025        if let Err(error) = self.borrow() {
1026            error.log_error();
1027        }
1028
1029        self
1030    }
1031
1032    fn map_collect<Y, T: FromIterator<Y>>(self, func: impl FnMut(Self::Item) -> Y) -> T
1033    where
1034        Self: IntoIterator + Sized,
1035    {
1036        self.into_iter().map(func).collect::<T>()
1037    }
1038
1039    fn map_into<Y, X: Into<Y>>(self) -> Option<Y>
1040    where
1041        Self: Is<Option<X>>,
1042    {
1043        self.into_self().map(X::into)
1044    }
1045
1046    fn map_as_ref<'a, Y: ?Sized, X: 'a + AsRef<Y>>(&'a self) -> Option<&'a Y>
1047    where
1048        Self: Borrow<Option<X>>,
1049    {
1050        self.borrow().as_ref().map(X::as_ref)
1051    }
1052
1053    fn mem_drop(self)
1054    where
1055        Self: Sized,
1056    {
1057        std::mem::drop(self);
1058    }
1059
1060    #[must_use]
1061    fn mem_replace(&mut self, value: Self) -> Self
1062    where
1063        Self: Sized,
1064    {
1065        std::mem::replace(self, value)
1066    }
1067
1068    #[must_use]
1069    fn mem_take(&mut self) -> Self
1070    where
1071        Self: Default,
1072    {
1073        std::mem::take(self)
1074    }
1075
1076    #[cfg(feature = "async")]
1077    async fn metadata_async(&self) -> Result<Metadata, IoError>
1078    where
1079        Self: AsRef<Path>,
1080    {
1081        tokio::fs::metadata(self).await
1082    }
1083
1084    // NOTE: [https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.push_mut]
1085    fn mut_push<T>(&mut self, item: T) -> &mut T
1086    where
1087        Self: BorrowMut<Vec<T>>,
1088    {
1089        let vec = self.borrow_mut();
1090
1091        vec.push(item);
1092
1093        vec.last_mut().unwrap()
1094    }
1095
1096    #[cfg(feature = "ropey")]
1097    fn num_lines_and_extended_graphemes<'a>(self) -> PointUsize
1098    where
1099        Self: Is<RopeSlice<'a>>,
1100    {
1101        let rope_slice = self.into_self();
1102        let y = rope_slice.len_lines();
1103        let x = rope_slice
1104            .lines()
1105            .map(|line_rope| line_rope.chunks().map(str::len_extended_graphemes).sum())
1106            .max()
1107            .unwrap_or(0);
1108
1109        PointUsize::new(x, y)
1110    }
1111
1112    fn ok<E>(self) -> Result<Self, E>
1113    where
1114        Self: Sized,
1115    {
1116        Ok(self)
1117    }
1118
1119    fn once(self) -> Once<Self>
1120    where
1121        Self: Sized,
1122    {
1123        std::iter::once(self)
1124    }
1125
1126    fn open(&self) -> Result<File, IoError>
1127    where
1128        Self: AsRef<Path>,
1129    {
1130        File::open(self)
1131    }
1132
1133    #[cfg(any(feature = "ropey", feature = "async"))]
1134    async fn open_async(&self) -> Result<TokioFile, IoError>
1135    where
1136        Self: AsRef<Path>,
1137    {
1138        TokioFile::open(self).await
1139    }
1140
1141    #[cfg(feature = "fmt")]
1142    fn optional_display(&self) -> OptionalDisplay<'_, Self> {
1143        OptionalDisplay::new(self)
1144    }
1145
1146    #[cfg(feature = "output")]
1147    fn output_ok<E>(self) -> Output<Self, E>
1148    where
1149        Self: Sized,
1150    {
1151        Output::Ok(self)
1152    }
1153
1154    fn owned<B: ?Sized + ToOwned<Owned = Self>>(self) -> Cow<'static, B>
1155    where
1156        Self: Sized,
1157    {
1158        Cow::Owned(self)
1159    }
1160
1161    fn pair<T>(self, rhs: T) -> (Self, T)
1162    where
1163        Self: Sized,
1164    {
1165        (self, rhs)
1166    }
1167
1168    fn pin(self) -> Pin<Box<Self>>
1169    where
1170        Self: Sized,
1171    {
1172        Box::pin(self)
1173    }
1174
1175    fn pipe<X, Y, Z, F: FnMut(Y) -> Z>(mut self, mut func: F) -> impl FnMut(X) -> Z
1176    where
1177        Self: Sized + FnMut(X) -> Y,
1178    {
1179        move |x| self(x).pipe_into(&mut func)
1180    }
1181
1182    fn poll_ready(self) -> Poll<Self>
1183    where
1184        Self: Sized,
1185    {
1186        Poll::Ready(self)
1187    }
1188
1189    fn pipe_into<T, F: FnOnce(Self) -> T>(self, func: F) -> T
1190    where
1191        Self: Sized,
1192    {
1193        func(self)
1194    }
1195
1196    #[cfg(feature = "poem")]
1197    fn poem_binary(self) -> PoemBinary<Self>
1198    where
1199        Self: Sized,
1200    {
1201        PoemBinary(self)
1202    }
1203
1204    #[cfg(feature = "poem")]
1205    fn poem_binary_message(self) -> PoemMessage
1206    where
1207        Self: Is<Vec<u8>>,
1208    {
1209        PoemMessage::Binary(self.into_self())
1210    }
1211
1212    #[cfg(feature = "poem")]
1213    fn poem_json(self) -> PoemJson<Self>
1214    where
1215        Self: Sized,
1216    {
1217        PoemJson(self)
1218    }
1219
1220    #[cfg(feature = "poem")]
1221    fn poem_stream_body<O: 'static + Into<Bytes>, E: 'static + Into<IoError>>(self) -> PoemBinary<PoemBody>
1222    where
1223        Self: 'static + Send + Sized + Stream<Item = Result<O, E>>,
1224    {
1225        PoemBody::from_bytes_stream(self).poem_binary()
1226    }
1227
1228    #[cfg(feature = "poem")]
1229    fn poem_text_message(self) -> PoemMessage
1230    where
1231        Self: Is<String>,
1232    {
1233        PoemMessage::Text(self.into_self())
1234    }
1235
1236    fn println(&self)
1237    where
1238        Self: Display,
1239    {
1240        std::println!("{self}");
1241    }
1242
1243    fn print(&self)
1244    where
1245        Self: Display,
1246    {
1247        std::print!("{self}");
1248    }
1249
1250    fn push_to<T: Extend<Self>>(self, collection: &mut T)
1251    where
1252        Self: Sized,
1253    {
1254        collection.extend(self.once());
1255    }
1256
1257    fn push_all_to<T: Extend<Self::Item>>(self, collection: &mut T)
1258    where
1259        Self: IntoIterator + Sized,
1260    {
1261        collection.extend(self);
1262    }
1263
1264    #[cfg(feature = "reqwest")]
1265    fn query_all<T: Serialize>(self, name: &str, values: impl IntoIterator<Item = T>) -> RequestBuilder
1266    where
1267        Self: Is<RequestBuilder>,
1268    {
1269        let mut request_builder = self.into_self();
1270
1271        for value in values {
1272            request_builder = request_builder.query_one(name, value);
1273        }
1274
1275        request_builder
1276    }
1277
1278    #[cfg(feature = "reqwest")]
1279    fn query_one<T: Serialize>(self, name: &str, value: impl Into<Option<T>>) -> RequestBuilder
1280    where
1281        Self: Is<RequestBuilder>,
1282    {
1283        let query: &[(&str, T)] = if let Some(value) = value.into() {
1284            &[(name, value)]
1285        } else {
1286            &[]
1287        };
1288        let request_builder = self.into_self();
1289
1290        request_builder.query(query)
1291    }
1292
1293    fn range_from_len(self, len: Self) -> Range<Self>
1294    where
1295        Self: Add<Output = Self> + Copy,
1296    {
1297        let end = self + len;
1298
1299        self..end
1300    }
1301
1302    #[cfg(feature = "tui")]
1303    fn ratatui_rect(self) -> Rect
1304    where
1305        Self: Into<(u16, u16)>,
1306    {
1307        let (width, height) = self.into();
1308        let x = 0;
1309        let y = 0;
1310
1311        Rect { x, y, width, height }
1312    }
1313
1314    #[cfg(any(feature = "async", feature = "process"))]
1315    async fn read_to_string_async(&mut self) -> Result<String, IoError>
1316    where
1317        Self: AsyncReadExt + Unpin,
1318    {
1319        let mut string = String::new();
1320
1321        self.read_to_string(&mut string).await?;
1322
1323        string.ok()
1324    }
1325
1326    #[cfg(feature = "async")]
1327    async fn read_to_string_fs_async(self) -> ReadValue<Self>
1328    where
1329        Self: AsRef<Path> + Sized,
1330    {
1331        let result = tokio::fs::read_to_string(self.as_ref()).await;
1332
1333        ReadValue::new(self, result)
1334    }
1335
1336    fn ready(self) -> Ready<Self>
1337    where
1338        Self: Sized,
1339    {
1340        std::future::ready(self)
1341    }
1342
1343    fn ref_immut(&self) -> &Self {
1344        self
1345    }
1346
1347    fn ref_mut(&mut self) -> &mut Self {
1348        self
1349    }
1350
1351    #[cfg(feature = "tui")]
1352    fn render_to(self, frame: &mut Frame, rect: Rect)
1353    where
1354        Self: Widget + Sized,
1355    {
1356        frame.render_widget(self, rect);
1357    }
1358
1359    fn repeat(self) -> Repeat<Self>
1360    where
1361        Self: Clone,
1362    {
1363        std::iter::repeat(self)
1364    }
1365
1366    fn reversed<X, Y>(self) -> (Y, X)
1367    where
1368        Self: Is<(X, Y)>,
1369    {
1370        let (x, y) = self.into_self();
1371
1372        y.pair(x)
1373    }
1374
1375    #[cfg(feature = "ropey")]
1376    async fn rope<const N: usize>(&self) -> Result<Rope, IoError>
1377    where
1378        Self: AsRef<Path>,
1379    {
1380        self.open_async()
1381            .await?
1382            .buf_reader_async()
1383            .pipe_into(RopeBuilder::<_, N>::new)
1384            .build()
1385            .await
1386    }
1387
1388    #[cfg(feature = "async")]
1389    async fn run_for(mut self, duration: Duration) -> Result<Self, RunForError<Self::Output>>
1390    where
1391        Self: Future + Sized + Unpin,
1392    {
1393        tokio::select! {
1394            output = &mut self => RunForError::new(output).err(),
1395            () = tokio::time::sleep(duration) => self.ok(),
1396        }
1397    }
1398
1399    #[cfg(feature = "async")]
1400    async fn run_local(self) -> Self::Output
1401    where
1402        Self: Future + Sized,
1403    {
1404        LocalSet::new().run_until(self).await
1405    }
1406
1407    fn remove_file(&self) -> Result<(), IoError>
1408    where
1409        Self: AsRef<Path>,
1410    {
1411        std::fs::remove_file(self)
1412    }
1413
1414    #[cfg(feature = "socket")]
1415    async fn respond_to<T: Request<Response = Self>>(
1416        &self,
1417        mut socket: impl BorrowMut<Socket>,
1418    ) -> Result<(), AnyhowError> {
1419        socket.borrow_mut().respond::<T>(self).await
1420    }
1421
1422    // TODO-ac2072:
1423    // - add [AsRopeSlice] trait that both [Rope] and [RopeSlice<'_>] implement
1424    // - i was doing this, but it didn't work due to some use of tempoarary variables error
1425    #[cfg(feature = "ropey")]
1426    fn saturating_chunks_at_extended_grapheme<'a>(self, extended_grapheme_index: usize) -> Chunks<'a>
1427    where
1428        Self: Is<RopeSlice<'a>>,
1429    {
1430        self.saturating_chunks_at_char(extended_grapheme_index)
1431    }
1432
1433    // TODO-ac2072
1434    #[cfg(feature = "ropey")]
1435    fn saturating_chunks_at_char<'a>(self, char_index: usize) -> Chunks<'a>
1436    where
1437        Self: Is<RopeSlice<'a>>,
1438    {
1439        let rope_slice = self.into_self();
1440        let char_index = rope_slice.len_chars().min(char_index);
1441
1442        rope_slice.chunks_at_char(char_index).0
1443    }
1444
1445    // TODO-ac2072
1446    #[cfg(feature = "ropey")]
1447    fn saturating_lines_at<'a>(self, line_index: usize) -> Lines<'a>
1448    where
1449        Self: Is<RopeSlice<'a>>,
1450    {
1451        let rope_slice = self.into_self();
1452        let line_index = rope_slice.len_lines().min(line_index);
1453
1454        rope_slice.lines_at(line_index)
1455    }
1456
1457    #[cfg(feature = "tui")]
1458    fn saturating_add_or_sub_in_place_with_max(&mut self, rhs: Self, max_value: Self, add: bool)
1459    where
1460        Self: Ord + SaturatingAdd + SaturatingSub + Sized,
1461    {
1462        let value = if add {
1463            self.saturating_add(&rhs)
1464        } else {
1465            self.saturating_sub(&rhs)
1466        };
1467
1468        *self = value.min(max_value);
1469    }
1470
1471    #[cfg(feature = "tui")]
1472    fn scroll_count(&self, scroll_count: ScrollCount, orientation: Orientation) -> usize
1473    where
1474        Self: Scrollable,
1475    {
1476        match scroll_count {
1477            ScrollCount::Fixed(scroll_count) => scroll_count,
1478            ScrollCount::PageSize => *self.latest_content_render_size().get(orientation),
1479        }
1480    }
1481
1482    #[cfg(feature = "tui")]
1483    fn max_scroll_offset(&self, scroll_when: ScrollWhen, content_size: PointUsize) -> PointUsize
1484    where
1485        Self: Scrollable,
1486    {
1487        match scroll_when {
1488            ScrollWhen::Always => content_size.saturating_sub_scalar(&1),
1489            ScrollWhen::ForLargeContent => content_size.saturating_sub(&self.latest_content_render_size()),
1490        }
1491    }
1492
1493    #[cfg(feature = "tui")]
1494    fn scroll(&mut self, scroll_count: ScrollCount, scroll_when: ScrollWhen, content_size: PointUsize, orientation: Orientation, add: bool)
1495    where
1496        Self: Scrollable,
1497    {
1498        let scroll_count = self.as_immut().scroll_count(scroll_count, orientation);
1499        let max_scroll_offset = *self.as_immut().max_scroll_offset(scroll_when, content_size).get(orientation);
1500
1501        self.scroll_offset_mut()
1502            .get_mut(orientation)
1503            .saturating_add_or_sub_in_place_with_max(scroll_count, max_scroll_offset, add);
1504    }
1505
1506    #[cfg(feature = "tui")]
1507    fn scroll_down(&mut self, scroll_count: ScrollCount, content_size: PointUsize, scroll_when: ScrollWhen)
1508    where
1509        Self: Scrollable,
1510    {
1511        self.scroll(scroll_count, scroll_when, content_size, Orientation::Vertical, true);
1512    }
1513
1514    #[cfg(feature = "tui")]
1515    fn scroll_up(&mut self, scroll_count: ScrollCount, content_size: PointUsize, scroll_when: ScrollWhen)
1516    where
1517        Self: Scrollable,
1518    {
1519        self.scroll(scroll_count, scroll_when, content_size, Orientation::Vertical, false);
1520    }
1521
1522    #[cfg(feature = "tui")]
1523    fn scroll_left(&mut self, scroll_count: ScrollCount, content_size: PointUsize, scroll_when: ScrollWhen)
1524    where
1525        Self: Scrollable,
1526    {
1527        self.scroll(scroll_count, scroll_when, content_size, Orientation::Horizontal, false);
1528    }
1529
1530    #[cfg(feature = "tui")]
1531    fn scroll_right(&mut self, scroll_count: ScrollCount, content_size: PointUsize, scroll_when: ScrollWhen)
1532    where
1533        Self: Scrollable,
1534    {
1535        self.scroll(scroll_count, scroll_when, content_size, Orientation::Horizontal, true);
1536    }
1537
1538    #[cfg(feature = "async")]
1539    async fn select_all(self) -> <<Self as IntoIterator>::Item as Future>::Output
1540    where
1541        Self: IntoIterator + Sized,
1542        <Self as IntoIterator>::Item: Future,
1543    {
1544        self.into_iter()
1545            .collect::<FuturesUnordered<_>>()
1546            .next()
1547            .wait_then_unwrap_or_pending()
1548            .await
1549    }
1550
1551    #[cfg(feature = "async")]
1552    async fn send_to<T: Sink<Self> + Unpin>(self, mut sink: T) -> Result<(), T::Error>
1553    where
1554        Self: Sized,
1555    {
1556        sink.send(self).await
1557    }
1558
1559    #[cfg(feature = "async")]
1560    fn send_to_oneshot(self, sender: OneshotSender<Self>) -> Result<(), AnyhowError>
1561    where
1562        Self: Sized,
1563    {
1564        // NOTE: drop error variant which wraps [Self] and may not implement [StdError]
1565        sender
1566            .send(self)
1567            .ok()
1568            .context("unable to send value over oneshot channel")
1569    }
1570
1571    fn set_true(&mut self) -> bool
1572    where
1573        Self: BorrowMut<bool>,
1574    {
1575        self.borrow_mut().mem_replace(true)
1576    }
1577
1578    fn set_false(&mut self) -> bool
1579    where
1580        Self: BorrowMut<bool>,
1581    {
1582        self.borrow_mut().mem_replace(false)
1583    }
1584
1585    #[cfg(feature = "async")]
1586    fn sleep(self) -> Sleep
1587    where
1588        Self: Is<Duration>,
1589    {
1590        tokio::time::sleep(self.into_self())
1591    }
1592
1593    fn some(self) -> Option<Self>
1594    where
1595        Self: Sized,
1596    {
1597        Some(self)
1598    }
1599
1600    #[cfg(feature = "async")]
1601    fn spawn_task(self) -> JoinHandle<Self::Output>
1602    where
1603        Self: 'static + Future + Sized + Send,
1604        Self::Output: 'static + Send,
1605    {
1606        tokio::spawn(self)
1607    }
1608
1609    // TODO-4eef0b: permit reverse search
1610    fn substr_interval(&self, query: &[u8]) -> Option<(usize, usize)>
1611    where
1612        Self: AsRef<[u8]>,
1613    {
1614        let byte_str = self.as_ref();
1615        let predicate = |substr| substr == query;
1616        let query_len = query.len();
1617        let begin = byte_str.windows(query_len).position(predicate)?;
1618        let end = begin + query_len;
1619
1620        (begin, end).some()
1621    }
1622
1623    #[cfg(feature = "serde")]
1624    fn take_json<T: DeserializeOwned>(&mut self, index: impl Index) -> Result<T, SerdeJsonError>
1625    where
1626        Self: BorrowMut<Json>,
1627    {
1628        self.borrow_mut()
1629            .get_mut(index)
1630            .unwrap_or(&mut Json::Null)
1631            .mem_take()
1632            .into_value_from_json()
1633    }
1634
1635    #[cfg(feature = "async")]
1636    fn timeout(self, duration: Duration) -> Timeout<Self>
1637    where
1638        Self: Future + Sized,
1639    {
1640        tokio::time::timeout(duration, self)
1641    }
1642
1643    fn toggle(&mut self)
1644    where
1645        Self: BorrowMut<bool>,
1646    {
1647        let bool_value = self.borrow_mut();
1648
1649        *bool_value = !*bool_value;
1650    }
1651
1652    #[cfg(feature = "serde")]
1653    fn to_json(&self) -> Result<Json, SerdeJsonError>
1654    where
1655        Self: Serialize,
1656    {
1657        serde_json::to_value(self)
1658    }
1659
1660    #[cfg(feature = "serde")]
1661    fn to_json_byte_str(&self) -> Result<Vec<u8>, SerdeJsonError>
1662    where
1663        Self: Serialize,
1664    {
1665        serde_json::to_vec(self)
1666    }
1667
1668    #[cfg(feature = "serde")]
1669    fn to_json_object(&self, key: &str) -> Json
1670    where
1671        Self: Serialize,
1672    {
1673        serde_json::json!({key: self})
1674    }
1675
1676    #[cfg(feature = "serde")]
1677    fn to_json_str(&self) -> Result<String, SerdeJsonError>
1678    where
1679        Self: Serialize,
1680    {
1681        serde_json::to_string(self)
1682    }
1683
1684    #[cfg(feature = "rmp")]
1685    fn to_rmp_byte_str(&self) -> Result<Vec<u8>, RmpEncodeError>
1686    where
1687        Self: Serialize,
1688    {
1689        rmp_serde::to_vec(self)
1690    }
1691
1692    #[cfg(feature = "fs")]
1693    fn to_uri(&self) -> Result<String, IoError>
1694    where
1695        Self: AsRef<Utf8Path>,
1696    {
1697        "file://".cat(self.absolute_utf8()?).ok()
1698    }
1699
1700    #[cfg(feature = "serde")]
1701    fn to_value_from_json_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, SerdeJsonError>
1702    where
1703        Self: AsRef<[u8]>,
1704    {
1705        serde_json::from_slice(self.as_ref())
1706    }
1707
1708    #[cfg(feature = "serde")]
1709    fn to_value_from_json_reader<T: DeserializeOwned>(self) -> Result<T, SerdeJsonError>
1710    where
1711        Self: Read + Sized,
1712    {
1713        serde_json::from_reader(self)
1714    }
1715
1716    #[cfg(feature = "rmp")]
1717    fn to_value_from_rmp_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, RmpDecodeError>
1718    where
1719        Self: AsRef<[u8]>,
1720    {
1721        rmp_serde::from_slice(self.as_ref())
1722    }
1723
1724    #[cfg(feature = "serde")]
1725    fn to_value_from_value<T: DeserializeOwned>(&self) -> Result<T, SerdeJsonError>
1726    where
1727        Self: Serialize,
1728    {
1729        self.to_json()?.into_value_from_json()
1730    }
1731
1732    #[cfg(feature = "serde")]
1733    fn to_value_from_yaml_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, SerdeYamlError>
1734    where
1735        Self: AsRef<[u8]>,
1736    {
1737        serde_yaml_ng::from_slice(self.as_ref())
1738    }
1739
1740    #[cfg(feature = "serde")]
1741    fn to_value_from_yaml_reader<T: DeserializeOwned>(self) -> Result<T, SerdeYamlError>
1742    where
1743        Self: Read + Sized,
1744    {
1745        serde_yaml_ng::from_reader(self)
1746    }
1747
1748    #[cfg(any(feature = "ropey", feature = "tui"))]
1749    #[must_use]
1750    fn transpose(&self) -> Self
1751    where
1752        Self: Transpose + Sized,
1753    {
1754        Transpose::to_transpose(self)
1755    }
1756
1757    fn try_convert<T: TryFrom<Self>>(self) -> Result<T, T::Error>
1758    where
1759        Self: Sized,
1760    {
1761        self.try_into()
1762    }
1763
1764    #[cfg(feature = "async")]
1765    async fn try_join_all<T, E>(self) -> Result<T, E>
1766    where
1767        Self: IntoIterator<Item: TryFuture> + Sized,
1768        T: FromIterator<<Self::Item as TryFuture>::Ok>,
1769        E: From<<Self::Item as TryFuture>::Error>,
1770    {
1771        futures::future::try_join_all(self)
1772            .await?
1773            .into_iter()
1774            .collect::<T>()
1775            .ok()
1776    }
1777
1778    #[cfg(feature = "async")]
1779    async fn try_wait<T, E: 'static + Send + Sync>(self) -> Result<T, AnyhowError>
1780    where
1781        Self: Is<JoinHandle<Result<T, E>>>,
1782        AnyhowError: From<E>,
1783    {
1784        self.into_self().await??.ok()
1785    }
1786
1787    #[must_use]
1788    fn type_name() -> &'static str {
1789        std::any::type_name::<Self>()
1790    }
1791
1792    fn unit(&self) {}
1793
1794    // Future<Option<T>>(). wait_for_some
1795    async fn wait_then_unwrap_or_pending<T>(self) -> T
1796    where
1797        Self: Future<Output = Option<T>> + Sized,
1798    {
1799        match self.await {
1800            Some(value) => value,
1801            None => std::future::pending().await,
1802        }
1803    }
1804
1805    // wait_if_some
1806    async fn unwrap_or_pending_then_wait<F: Future + Unpin>(&mut self) -> F::Output
1807    where
1808        Self: BorrowMut<Option<F>>,
1809    {
1810        if let Some(future) = self.borrow_mut().as_mut() {
1811            future.await
1812        } else {
1813            std::future::pending().await
1814        }
1815    }
1816
1817    fn with<T>(&self, value: T) -> T {
1818        value
1819    }
1820
1821    fn with_item_pushed<T>(self, item: T) -> Vec<T>
1822    where
1823        Self: Is<Vec<T>>,
1824    {
1825        let mut vec = self.into_self();
1826
1827        vec.push(item);
1828
1829        vec
1830    }
1831
1832    fn with_str_pushed(self, rhs: &str) -> String
1833    where
1834        Self: Is<String>,
1835    {
1836        let mut string = self.into_self();
1837
1838        string.push_str(rhs);
1839
1840        string
1841    }
1842
1843    #[cfg(feature = "serde")]
1844    fn write_as_json_to<T: Write>(&self, writer: T) -> Result<(), SerdeJsonError>
1845    where
1846        Self: Serialize,
1847    {
1848        serde_json::to_writer(writer, self)
1849    }
1850
1851    fn write_all_then(&mut self, byte_str: &[u8]) -> Result<&mut Self, IoError>
1852    where
1853        Self: Write,
1854    {
1855        self.write_all(byte_str)?.with(self).ok()
1856    }
1857
1858    #[cfg(any(feature = "async", feature = "process"))]
1859    async fn write_all_then_async(&mut self, byte_str: &[u8]) -> Result<&mut Self, IoError>
1860    where
1861        Self: AsyncWriteExt + Unpin,
1862    {
1863        self.write_all(byte_str).await?.with(self).ok()
1864    }
1865}
1866
1867impl<T: ?Sized> Utils for T {}