mkutils/
utils.rs

1#[cfg(feature = "serde")]
2use crate::as_valuable::AsValuable;
3#[cfg(feature = "fmt")]
4use crate::fmt::{Debugged, OptionalDisplay};
5#[cfg(feature = "ropey")]
6use crate::geometry::PointUsize;
7use crate::is::Is;
8#[cfg(any(feature = "serde", feature = "tui"))]
9use crate::seq_visitor::SeqVisitor;
10#[cfg(feature = "socket")]
11use crate::socket::{Request, Socket};
12#[cfg(feature = "tracing")]
13use crate::status::Status;
14#[cfg(feature = "async")]
15use crate::{into_stream::IntoStream, read_value::ReadValue, run_for::RunForError};
16#[cfg(any(
17    feature = "async",
18    feature = "fs",
19    feature = "process",
20    feature = "reqwest",
21    feature = "socket",
22    feature = "tui",
23))]
24use anyhow::{Context, Error as AnyhowError};
25#[cfg(feature = "async")]
26use bytes::Buf;
27#[cfg(feature = "poem")]
28use bytes::Bytes;
29#[cfg(feature = "fs")]
30use camino::{Utf8Path, Utf8PathBuf};
31#[cfg(any(feature = "async", feature = "poem"))]
32use futures::Stream;
33#[cfg(feature = "async")]
34use futures::{Sink, SinkExt, StreamExt, TryFuture, future::Either, stream::Filter};
35#[cfg(feature = "tui")]
36use num::traits::{SaturatingAdd, SaturatingSub};
37#[cfg(feature = "poem")]
38use poem::{Body as PoemBody, Endpoint, IntoResponse, web::websocket::Message as PoemMessage};
39#[cfg(feature = "poem")]
40use poem_openapi::{error::ParseRequestPayloadError, payload::Binary as PoemBinary, payload::Json as PoemJson};
41#[cfg(feature = "tui")]
42use ratatui::{
43    layout::Rect,
44    text::{Line, Span},
45};
46#[cfg(feature = "reqwest")]
47use reqwest::{RequestBuilder, Response};
48#[cfg(feature = "rmp")]
49use rmp_serde::{decode::Error as RmpDecodeError, encode::Error as RmpEncodeError};
50#[cfg(feature = "ropey")]
51use ropey::{Rope, RopeSlice, iter::Chunks, iter::Lines};
52#[cfg(any(feature = "rmp", feature = "serde", feature = "tui"))]
53use serde::Deserialize;
54#[cfg(any(feature = "serde", feature = "tui"))]
55use serde::Deserializer;
56#[cfg(any(feature = "reqwest", feature = "rmp", feature = "serde"))]
57use serde::Serialize;
58#[cfg(feature = "serde")]
59use serde::de::DeserializeOwned;
60#[cfg(any(feature = "poem", feature = "serde"))]
61use serde_json::Error as SerdeJsonError;
62#[cfg(feature = "serde")]
63use serde_json::{Value as Json, value::Index};
64#[cfg(feature = "serde")]
65use serde_yaml_ng::Error as SerdeYamlError;
66use std::{
67    borrow::{Borrow, BorrowMut, Cow},
68    collections::HashMap,
69    error::Error as StdError,
70    fmt::Display,
71    fs::File,
72    future::Ready,
73    hash::Hash,
74    io::{BufReader, BufWriter, Error as IoError, Read, Write},
75    iter::{Once, Repeat},
76    mem::ManuallyDrop,
77    ops::{Add, ControlFlow, Range},
78    path::Path,
79    pin::Pin,
80    str::Utf8Error,
81    sync::{
82        Arc,
83        atomic::{AtomicUsize, Ordering},
84    },
85    task::Poll,
86    time::Instant,
87};
88#[cfg(feature = "fs")]
89use std::{fmt::Debug, path::PathBuf};
90#[cfg(feature = "async")]
91use std::{fs::Metadata, time::Duration};
92#[cfg(any(feature = "async", feature = "ropey"))]
93use tokio::{
94    fs::File as TokioFile,
95    io::{AsyncRead, BufReader as TokioBufReader},
96};
97#[cfg(feature = "async")]
98use tokio::{
99    io::{AsyncReadExt, AsyncWrite, AsyncWriteExt, BufWriter as TokioBufWriter},
100    sync::oneshot::Sender as OneshotSender,
101    task::JoinHandle,
102    task::{JoinSet, LocalSet},
103    time::{Interval, Sleep, Timeout},
104};
105#[cfg(feature = "async")]
106use tokio_util::{
107    codec::{Framed, LengthDelimitedCodec, LinesCodec},
108    io::StreamReader,
109};
110#[cfg(feature = "tracing")]
111use tracing::Level;
112#[cfg(any(feature = "ropey", feature = "tui"))]
113use unicode_segmentation::{Graphemes, UnicodeSegmentation};
114#[cfg(feature = "serde")]
115use valuable::Value;
116
117#[allow(async_fn_in_trait)]
118pub trait Utils {
119    const NEWLINE: &str = "\n";
120
121    #[cfg(feature = "async")]
122    async fn abort_all_and_wait<T: 'static>(&mut self)
123    where
124        Self: BorrowMut<JoinSet<T>>,
125    {
126        let join_set = self.borrow_mut();
127
128        join_set.abort_all();
129
130        while join_set.join_next().await.is_some() {}
131    }
132
133    #[cfg(feature = "fs")]
134    fn absolute_utf8(&self) -> Result<Cow<'_, Utf8Path>, IoError>
135    where
136        Self: AsRef<Utf8Path>,
137    {
138        let path = self.as_ref();
139
140        if path.is_absolute() {
141            path.borrowed().ok()
142        } else {
143            camino::absolute_utf8(path)?.owned::<Utf8Path>().ok()
144        }
145    }
146
147    async fn achain<T: Future>(self, rhs: T) -> T::Output
148    where
149        Self: Future + Sized,
150    {
151        self.await;
152
153        rhs.await
154    }
155
156    #[cfg(feature = "tui")]
157    fn add_span<'a, T: Into<Span<'a>>>(self, span: T) -> Line<'a>
158    where
159        Self: Into<Line<'a>>,
160    {
161        let mut line = self.into();
162
163        line.spans.push(span.into());
164
165        line
166    }
167
168    #[cfg(any(
169        feature = "async",
170        feature = "fs",
171        feature = "process",
172        feature = "reqwest",
173        feature = "socket",
174        feature = "tui",
175    ))]
176    fn anyhow_result<T, E: Into<AnyhowError>>(self) -> Result<T, AnyhowError>
177    where
178        Self: Is<Result<T, E>>,
179    {
180        self.into_self().map_err(E::into)
181    }
182
183    fn arc(self) -> Arc<Self>
184    where
185        Self: Sized,
186    {
187        Arc::new(self)
188    }
189
190    async fn async_with<T>(self, next: impl Future<Output = T>) -> T
191    where
192        Self: Future + Sized,
193    {
194        self.await;
195
196        next.await
197    }
198
199    fn as_borrowed<'a, B: ?Sized + ToOwned>(&'a self) -> &'a B
200    where
201        Self: Borrow<Cow<'a, B>>,
202    {
203        self.borrow().borrow()
204    }
205
206    fn as_ptr(&self) -> *const Self {
207        std::ptr::from_ref(self)
208    }
209
210    fn as_ptr_mut(&mut self) -> *mut Self {
211        std::ptr::from_mut(self)
212    }
213
214    fn as_utf8(&self) -> Result<&str, Utf8Error>
215    where
216        Self: AsRef<[u8]>,
217    {
218        std::str::from_utf8(self.as_ref())
219    }
220
221    #[cfg(feature = "fs")]
222    fn as_utf8_path(&self) -> &Utf8Path
223    where
224        Self: AsRef<Utf8Path>,
225    {
226        self.as_ref()
227    }
228
229    #[cfg(feature = "ropey")]
230    fn as_slice(&self) -> RopeSlice<'_>
231    where
232        Self: Borrow<Rope>,
233    {
234        self.borrow().slice(..)
235    }
236
237    #[cfg(feature = "serde")]
238    fn as_valuable(&self) -> Value<'_>
239    where
240        Self: AsValuable,
241    {
242        AsValuable::as_valuable(self)
243    }
244
245    fn borrowed(&self) -> Cow<'_, Self>
246    where
247        Self: ToOwned,
248    {
249        Cow::Borrowed(self)
250    }
251
252    fn buf_reader(self) -> BufReader<Self>
253    where
254        Self: Read + Sized,
255    {
256        BufReader::new(self)
257    }
258
259    #[cfg(any(feature = "async", feature = "ropey"))]
260    fn buf_reader_async(self) -> TokioBufReader<Self>
261    where
262        Self: AsyncRead + Sized,
263    {
264        TokioBufReader::new(self)
265    }
266
267    fn buf_writer(self) -> BufWriter<Self>
268    where
269        Self: Write + Sized,
270    {
271        BufWriter::new(self)
272    }
273
274    #[cfg(feature = "async")]
275    fn buf_writer_async(self) -> TokioBufWriter<Self>
276    where
277        Self: AsyncWrite + Sized,
278    {
279        TokioBufWriter::new(self)
280    }
281
282    // NOTE: requires that [Self] and [T] have the same layout (provided by [#[repr(transparent)]]):
283    // [https://stackoverflow.com/questions/79593399/implement-valuablevaluable-on-serde-jsonvalue]
284    fn cast_ref<T>(&self) -> &T {
285        let ptr = std::ptr::from_ref(self).cast::<T>();
286        let value = unsafe { ptr.as_ref() };
287
288        value.unwrap()
289    }
290
291    fn cat<T: Display>(&self, rhs: T) -> String
292    where
293        Self: Display,
294    {
295        std::format!("{self}{rhs}")
296    }
297
298    #[cfg(any(
299        feature = "async",
300        feature = "fs",
301        feature = "process",
302        feature = "reqwest",
303        feature = "socket",
304        feature = "tui",
305    ))]
306    fn check_next<T>(self) -> Result<T, AnyhowError>
307    where
308        Self: Is<Option<T>>,
309    {
310        match self.into_self() {
311            Some(item) => item.ok(),
312            None => anyhow::bail!(
313                "sequence of {type_name} items is exhausted",
314                type_name = Self::type_name(),
315            ),
316        }
317    }
318
319    #[cfg(any(
320        feature = "async",
321        feature = "fs",
322        feature = "process",
323        feature = "reqwest",
324        feature = "socket",
325        feature = "tui",
326    ))]
327    fn check_present<T>(self) -> Result<T, AnyhowError>
328    where
329        Self: Is<Option<T>>,
330    {
331        match self.into_self() {
332            Some(item) => item.ok(),
333            None => anyhow::bail!(
334                "keyed entry of type {type_name} is not present in the collection",
335                type_name = Self::type_name(),
336            ),
337        }
338    }
339
340    // NOTE: [https://docs.rs/reqwest/latest/reqwest/struct.Response.html#method.error_for_status]
341    #[cfg(feature = "reqwest")]
342    async fn check_status(self) -> Result<Response, AnyhowError>
343    where
344        Self: Is<Response>,
345    {
346        let response = self.into_self();
347        let status = response.status();
348
349        if !status.is_client_error() && !status.is_server_error() {
350            return response.ok();
351        }
352
353        let text = match response.text().await {
354            Ok(text) => text,
355            Err(error) => std::format!("unable to read response text: {error}"),
356        };
357
358        anyhow::bail!("({status}) {text}")
359    }
360
361    fn convert<T: From<Self>>(self) -> T
362    where
363        Self: Sized,
364    {
365        self.into()
366    }
367
368    fn find_eq<Q, K>(&self, query: Q) -> Option<(usize, &K)>
369    where
370        Self: AsRef<[K]>,
371        for<'a> &'a K: PartialEq<Q>,
372    {
373        self.as_ref().iter().enumerate().find(|(_index, key)| *key == query)
374    }
375
376    fn contains_eq<Q, K>(&self, query: Q) -> bool
377    where
378        Self: AsRef<[K]>,
379        for<'a> &'a K: PartialEq<Q>,
380    {
381        self.find_eq(query).is_some()
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 context_path<T, E, C: 'static + Display + Send + Sync, P: AsRef<Path>>(
393        self,
394        context: C,
395        path: P,
396    ) -> Result<T, AnyhowError>
397    where
398        Self: Context<T, E> + Sized,
399    {
400        let context = std::format!("{context}: {path}", path = path.as_ref().display());
401
402        self.context(context)
403    }
404
405    fn create(&self) -> Result<File, IoError>
406    where
407        Self: AsRef<Path>,
408    {
409        File::create(self)
410    }
411
412    fn create_dir_all(&self) -> Result<(), IoError>
413    where
414        Self: AsRef<Path>,
415    {
416        std::fs::create_dir_all(self)
417    }
418
419    #[cfg(feature = "fmt")]
420    fn debug(&self) -> Debugged<'_, Self> {
421        Debugged::new(self)
422    }
423
424    #[cfg(any(feature = "serde", feature = "tui"))]
425    fn deserialize_from_seq<'de, D: Deserializer<'de>, X: Deserialize<'de>, Y, E: Display, F: Fn(X) -> Result<Y, E>>(
426        deserializer: D,
427        func: F,
428    ) -> Result<Self, D::Error>
429    where
430        Self: Default + Extend<Y>,
431    {
432        let seq_visitor = SeqVisitor::new(func);
433
434        deserializer.deserialize_seq(seq_visitor)
435    }
436
437    fn err<T>(self) -> Result<T, Self>
438    where
439        Self: Sized,
440    {
441        Err(self)
442    }
443
444    #[cfg(feature = "fs")]
445    fn expand_user(&self) -> Cow<'_, Utf8Path>
446    where
447        Self: AsRef<str>,
448    {
449        let path_str = self.as_ref();
450
451        if let Some(relative_path_str) = path_str.strip_prefix("~/")
452            && let Some(home_dirpath) = Self::home_dirpath()
453        {
454            home_dirpath.join(relative_path_str).owned()
455        } else {
456            path_str.as_utf8_path().borrowed()
457        }
458    }
459
460    #[cfg(feature = "fs")]
461    fn unexpand_user(&self) -> Cow<'_, Utf8Path>
462    where
463        Self: AsRef<str>,
464    {
465        let path_str = self.as_ref();
466
467        if let Some(home_dirpath) = Self::home_dirpath()
468            && let Some(relative_path_str) = path_str.strip_prefix(home_dirpath.as_str())
469        {
470            if relative_path_str.is_empty() {
471                "~".convert::<Utf8PathBuf>().owned()
472            } else if relative_path_str.as_utf8_path().is_absolute() {
473                "~".cat(relative_path_str).convert::<Utf8PathBuf>().owned()
474            } else {
475                path_str.as_utf8_path().borrowed()
476            }
477        } else {
478            path_str.as_utf8_path().borrowed()
479        }
480    }
481
482    #[cfg(any(feature = "ropey", feature = "tui"))]
483    fn extended_graphemes(&self) -> Graphemes<'_>
484    where
485        Self: AsRef<str>,
486    {
487        self.as_ref().graphemes(true)
488    }
489
490    #[cfg(feature = "ropey")]
491    fn extended_graphemes_at<'a>(self, extended_graphemes_index_range: Range<usize>) -> impl Iterator<Item = &'a str>
492    where
493        Self: Is<RopeSlice<'a>>,
494    {
495        let extended_graphemes_index_range = extended_graphemes_index_range.borrow();
496
497        self.into_self()
498            .chunks()
499            .flat_map(str::extended_graphemes)
500            .skip(extended_graphemes_index_range.start)
501            .take(extended_graphemes_index_range.len())
502    }
503
504    #[cfg(feature = "ropey")]
505    fn extended_graphemes_at_rect<'a>(
506        self,
507        lines_index_range: Range<usize>,
508        extended_graphemes_index_range: Range<usize>,
509    ) -> impl Iterator<Item = impl Iterator<Item = &'a str>>
510    where
511        Self: Is<RopeSlice<'a>>,
512    {
513        self.into_self()
514            .saturating_lines_at(lines_index_range.start)
515            .take(lines_index_range.len())
516            .map(move |line_rope_slice| line_rope_slice.extended_graphemes_at(extended_graphemes_index_range.clone()))
517    }
518
519    #[cfg(feature = "async")]
520    fn filter_sync(
521        self,
522        mut func: impl FnMut(&Self::Item) -> bool,
523    ) -> Filter<Self, Ready<bool>, impl FnMut(&Self::Item) -> Ready<bool>>
524    where
525        Self: Sized + StreamExt,
526    {
527        // TODO: figure out why [func.pipe(bool::ready)] won't work
528        self.filter(move |x| func(x).ready())
529    }
530
531    #[cfg(feature = "fs")]
532    fn file_name_ok(&self) -> Result<&str, AnyhowError>
533    where
534        Self: AsRef<Utf8Path>,
535    {
536        self.as_ref().file_name().context("path has no file name")
537    }
538
539    fn has_happened(self) -> bool
540    where
541        Self: Is<Instant>,
542    {
543        self.into_self() <= Instant::now()
544    }
545
546    #[cfg(feature = "fs")]
547    #[must_use]
548    fn home_dirpath() -> Option<Utf8PathBuf> {
549        home::home_dir()?.try_convert::<Utf8PathBuf>().ok()
550    }
551
552    fn if_else<T>(self, true_value: T, false_value: T) -> T
553    where
554        Self: Is<bool>,
555    {
556        if self.into_self() { true_value } else { false_value }
557    }
558
559    fn immutable(&mut self) -> &Self {
560        self
561    }
562
563    fn inc(&self) -> usize
564    where
565        Self: Borrow<AtomicUsize>,
566    {
567        self.borrow().fetch_add(1, Ordering::SeqCst)
568    }
569
570    fn insert_mut<'a, K: 'a + Eq + Hash, V>(&'a mut self, key: K, value: V) -> &'a mut V
571    where
572        Self: BorrowMut<HashMap<K, V>>,
573    {
574        self.borrow_mut().entry(key).insert_entry(value).into_mut()
575    }
576
577    fn into_break<C>(self) -> ControlFlow<Self, C>
578    where
579        Self: Sized,
580    {
581        ControlFlow::Break(self)
582    }
583
584    fn into_continue<B>(self) -> ControlFlow<B, Self>
585    where
586        Self: Sized,
587    {
588        ControlFlow::Continue(self)
589    }
590
591    #[cfg(feature = "poem")]
592    fn into_endpoint(self) -> impl Endpoint<Output = Self>
593    where
594        Self: Clone + IntoResponse + Sync,
595    {
596        let func = move |_request| self.clone();
597
598        poem::endpoint::make_sync(func)
599    }
600
601    #[cfg(feature = "async")]
602    fn into_interval(self) -> Interval
603    where
604        Self: Is<Duration>,
605    {
606        tokio::time::interval(self.into_self())
607    }
608
609    #[cfg(feature = "async")]
610    fn into_left<R>(self) -> Either<Self, R>
611    where
612        Self: Sized,
613    {
614        Either::Left(self)
615    }
616
617    #[cfg(feature = "tui")]
618    fn into_line<'a>(self) -> Line<'a>
619    where
620        Self: Into<Cow<'a, str>>,
621    {
622        self.into().into()
623    }
624
625    fn into_manually_drop(self) -> ManuallyDrop<Self>
626    where
627        Self: Sized,
628    {
629        ManuallyDrop::new(self)
630    }
631
632    #[cfg(feature = "async")]
633    fn into_stream_reader<B: Buf, E: Into<IoError>>(self) -> StreamReader<Self, B>
634    where
635        Self: Sized + Stream<Item = Result<B, E>>,
636    {
637        StreamReader::new(self)
638    }
639
640    #[cfg(feature = "async")]
641    fn into_length_delimited_frames(self) -> Framed<Self, LengthDelimitedCodec>
642    where
643        Self: Sized,
644    {
645        Framed::new(self, LengthDelimitedCodec::new())
646    }
647
648    #[cfg(feature = "async")]
649    fn into_line_frames(self) -> Framed<Self, LinesCodec>
650    where
651        Self: Sized,
652    {
653        Framed::new(self, LinesCodec::new())
654    }
655
656    #[cfg(feature = "async")]
657    fn into_right<L>(self) -> Either<L, Self>
658    where
659        Self: Sized,
660    {
661        Either::Right(self)
662    }
663
664    // NOTE: [https://docs.rs/poem-openapi/latest/src/poem_openapi/payload/json.rs.html]
665    #[cfg(feature = "poem")]
666    fn into_parse_request_payload_result<T>(self) -> Result<T, ParseRequestPayloadError>
667    where
668        Self: Is<Result<T, SerdeJsonError>>,
669    {
670        match self.into_self() {
671            Ok(value) => value.ok(),
672            Err(serde_json_error) => ParseRequestPayloadError {
673                reason: serde_json_error.to_string(),
674            }
675            .err(),
676        }
677    }
678
679    #[cfg(feature = "async")]
680    async fn into_select<T: Future>(self, rhs: T) -> Either<Self::Output, T::Output>
681    where
682        Self: Future + Sized,
683    {
684        tokio::select! {
685            value = self => value.into_left(),
686            value = rhs => value.into_right(),
687        }
688    }
689
690    #[cfg(feature = "tracing")]
691    fn into_status<T, E>(self) -> Status<T, E>
692    where
693        Self: Is<Result<T, E>>,
694    {
695        Status::new(self.into_self())
696    }
697
698    #[cfg(feature = "async")]
699    fn into_stream(self) -> Self::Stream
700    where
701        Self: IntoStream + Sized,
702    {
703        IntoStream::into_stream(self)
704    }
705
706    #[cfg(feature = "fs")]
707    fn into_string(self) -> Result<String, AnyhowError>
708    where
709        Self: Is<PathBuf>,
710    {
711        match self.into_self().into_os_string().into_string() {
712            Ok(string) => string.ok(),
713            Err(os_string) => os_string.invalid_utf8_err(),
714        }
715    }
716
717    #[cfg(feature = "serde")]
718    fn into_value_from_json<T: DeserializeOwned>(self) -> Result<T, SerdeJsonError>
719    where
720        Self: Is<Json>,
721    {
722        serde_json::from_value(self.into_self())
723    }
724
725    #[cfg(feature = "fs")]
726    fn invalid_utf8_err<T>(&self) -> Result<T, AnyhowError>
727    where
728        Self: Debug,
729    {
730        anyhow::bail!("{self:?} is not valid utf-8")
731    }
732
733    fn io_error(self) -> IoError
734    where
735        Self: Into<Box<dyn StdError + Send + Sync>>,
736    {
737        IoError::other(self)
738    }
739
740    fn io_result<T, E: Into<Box<dyn StdError + Send + Sync>>>(self) -> Result<T, IoError>
741    where
742        Self: Is<Result<T, E>>,
743    {
744        self.into_self().map_err(E::io_error)
745    }
746
747    #[cfg(feature = "async")]
748    async fn join_all<T>(self) -> T
749    where
750        Self: IntoIterator<Item: Future> + Sized,
751        T: FromIterator<<Self::Item as Future>::Output>,
752    {
753        futures::future::join_all(self).await.into_iter().collect()
754    }
755
756    #[cfg(any(feature = "ropey", feature = "tui"))]
757    fn len_extended_graphemes(&self) -> usize
758    where
759        Self: AsRef<str>,
760    {
761        self.as_ref().extended_graphemes().count()
762    }
763
764    #[cfg(feature = "tracing")]
765    fn level<T, E>(&self) -> Level
766    where
767        Self: Borrow<Result<T, E>>,
768    {
769        if self.borrow().is_ok() {
770            Level::INFO
771        } else {
772            Level::WARN
773        }
774    }
775
776    #[cfg(any(feature = "tui", feature = "tracing"))]
777    fn log_error(&self)
778    where
779        Self: Display,
780    {
781        tracing::warn!(error = %self, "error: {self:#}");
782    }
783
784    #[cfg(any(feature = "tui", feature = "tracing"))]
785    #[must_use]
786    fn log_if_error<T, E: Display>(self) -> Self
787    where
788        Self: Borrow<Result<T, E>> + Sized,
789    {
790        if let Err(error) = self.borrow() {
791            error.log_error();
792        }
793
794        self
795    }
796
797    fn map_collect<Y, T: FromIterator<Y>>(self, func: impl FnMut(Self::Item) -> Y) -> T
798    where
799        Self: IntoIterator + Sized,
800    {
801        self.into_iter().map(func).collect::<T>()
802    }
803
804    fn map_into<Y, X: Into<Y>>(self) -> Option<Y>
805    where
806        Self: Is<Option<X>>,
807    {
808        self.into_self().map(X::into)
809    }
810
811    fn map_as_ref<'a, Y: ?Sized, X: 'a + AsRef<Y>>(&'a self) -> Option<&'a Y>
812    where
813        Self: Borrow<Option<X>>,
814    {
815        self.borrow().as_ref().map(X::as_ref)
816    }
817
818    fn mem_drop(self)
819    where
820        Self: Sized,
821    {
822        std::mem::drop(self);
823    }
824
825    #[must_use]
826    fn mem_take(&mut self) -> Self
827    where
828        Self: Default,
829    {
830        std::mem::take(self)
831    }
832
833    #[cfg(feature = "async")]
834    async fn metadata_async(&self) -> Result<Metadata, IoError>
835    where
836        Self: AsRef<Path>,
837    {
838        tokio::fs::metadata(self).await
839    }
840
841    #[cfg(feature = "ropey")]
842    fn num_lines_and_extended_graphemes<'a>(self) -> PointUsize
843    where
844        Self: Is<RopeSlice<'a>>,
845    {
846        let rope_slice = self.into_self();
847        let y = rope_slice.len_lines();
848        let x = rope_slice
849            .lines()
850            .map(|line_rope| line_rope.chunks().map(str::len_extended_graphemes).sum())
851            .max()
852            .unwrap_or(0);
853
854        PointUsize::new(x, y)
855    }
856
857    fn ok<E>(self) -> Result<Self, E>
858    where
859        Self: Sized,
860    {
861        Ok(self)
862    }
863
864    fn once(self) -> Once<Self>
865    where
866        Self: Sized,
867    {
868        std::iter::once(self)
869    }
870
871    fn open(&self) -> Result<File, IoError>
872    where
873        Self: AsRef<Path>,
874    {
875        File::open(self)
876    }
877
878    #[cfg(any(feature = "ropey", feature = "async"))]
879    async fn open_async(&self) -> Result<TokioFile, IoError>
880    where
881        Self: AsRef<Path>,
882    {
883        TokioFile::open(self).await
884    }
885
886    #[cfg(feature = "fmt")]
887    fn optional_display(&self) -> OptionalDisplay<'_, Self> {
888        OptionalDisplay::new(self)
889    }
890
891    fn owned<B: ?Sized + ToOwned<Owned = Self>>(self) -> Cow<'static, B>
892    where
893        Self: Sized,
894    {
895        Cow::Owned(self)
896    }
897
898    fn pair<T>(self, rhs: T) -> (Self, T)
899    where
900        Self: Sized,
901    {
902        (self, rhs)
903    }
904
905    fn pin(self) -> Pin<Box<Self>>
906    where
907        Self: Sized,
908    {
909        Box::pin(self)
910    }
911
912    fn pipe<X, Y, Z, F: FnMut(Y) -> Z>(mut self, mut func: F) -> impl FnMut(X) -> Z
913    where
914        Self: Sized + FnMut(X) -> Y,
915    {
916        move |x| self(x).pipe_into(&mut func)
917    }
918
919    fn poll_ready(self) -> Poll<Self>
920    where
921        Self: Sized,
922    {
923        Poll::Ready(self)
924    }
925
926    fn pipe_into<T, F: FnOnce(Self) -> T>(self, func: F) -> T
927    where
928        Self: Sized,
929    {
930        func(self)
931    }
932
933    #[cfg(feature = "poem")]
934    fn poem_binary(self) -> PoemBinary<Self>
935    where
936        Self: Sized,
937    {
938        PoemBinary(self)
939    }
940
941    #[cfg(feature = "poem")]
942    fn poem_binary_message(self) -> PoemMessage
943    where
944        Self: Is<Vec<u8>>,
945    {
946        PoemMessage::Binary(self.into_self())
947    }
948
949    #[cfg(feature = "poem")]
950    fn poem_json(self) -> PoemJson<Self>
951    where
952        Self: Sized,
953    {
954        PoemJson(self)
955    }
956
957    #[cfg(feature = "poem")]
958    fn poem_stream_body<O: 'static + Into<Bytes>, E: 'static + Into<IoError>>(self) -> PoemBinary<PoemBody>
959    where
960        Self: 'static + Send + Sized + Stream<Item = Result<O, E>>,
961    {
962        PoemBody::from_bytes_stream(self).poem_binary()
963    }
964
965    #[cfg(feature = "poem")]
966    fn poem_text_message(self) -> PoemMessage
967    where
968        Self: Is<String>,
969    {
970        PoemMessage::Text(self.into_self())
971    }
972
973    fn println(&self)
974    where
975        Self: Display,
976    {
977        std::println!("{self}");
978    }
979
980    fn print(&self)
981    where
982        Self: Display,
983    {
984        std::print!("{self}");
985    }
986
987    fn push_to<T: Extend<Self>>(self, collection: &mut T)
988    where
989        Self: Sized,
990    {
991        collection.extend(self.once());
992    }
993
994    #[cfg(feature = "reqwest")]
995    fn query_all<T: Serialize>(self, name: &str, values: impl IntoIterator<Item = T>) -> RequestBuilder
996    where
997        Self: Is<RequestBuilder>,
998    {
999        let mut request_builder = self.into_self();
1000
1001        for value in values {
1002            request_builder = request_builder.query_one(name, value);
1003        }
1004
1005        request_builder
1006    }
1007
1008    #[cfg(feature = "reqwest")]
1009    fn query_one<T: Serialize>(self, name: &str, value: impl Into<Option<T>>) -> RequestBuilder
1010    where
1011        Self: Is<RequestBuilder>,
1012    {
1013        let query: &[(&str, T)] = if let Some(value) = value.into() {
1014            &[(name, value)]
1015        } else {
1016            &[]
1017        };
1018        let request_builder = self.into_self();
1019
1020        request_builder.query(query)
1021    }
1022
1023    fn range_from_len<T: Add<Output = T> + Copy>(self, len: impl Into<T>) -> Range<T>
1024    where
1025        Self: Into<T>,
1026    {
1027        let start = self.into();
1028        let end = start + len.into();
1029
1030        start..end
1031    }
1032
1033    #[cfg(feature = "tui")]
1034    fn ratatui_rect(self) -> Rect
1035    where
1036        Self: Into<(u16, u16)>,
1037    {
1038        let (width, height) = self.into();
1039        let x = 0;
1040        let y = 0;
1041
1042        Rect { x, y, width, height }
1043    }
1044
1045    #[cfg(feature = "async")]
1046    async fn read_string_async(&mut self) -> Result<String, IoError>
1047    where
1048        Self: AsyncReadExt + Unpin,
1049    {
1050        let mut string = String::new();
1051
1052        self.read_to_string(&mut string).await?;
1053
1054        string.ok()
1055    }
1056
1057    #[cfg(feature = "async")]
1058    async fn read_to_string_async(self) -> ReadValue<Self>
1059    where
1060        Self: AsRef<Path> + Sized,
1061    {
1062        let result = tokio::fs::read_to_string(self.as_ref()).await;
1063
1064        ReadValue::new(self, result)
1065    }
1066
1067    #[cfg(feature = "async")]
1068    async fn read_to_string_else_stdin_async<P: AsRef<Path>>(self) -> ReadValue<Option<P>>
1069    where
1070        Self: Is<Option<P>> + Sized,
1071    {
1072        if let Some(filepath) = self.into_self() {
1073            tokio::fs::read_to_string(filepath.as_ref()).await.pair(filepath.some())
1074        } else {
1075            tokio::io::stdin()
1076                .buf_reader_async()
1077                .read_string_async()
1078                .await
1079                .pair(None)
1080        }
1081        .reversed()
1082        .into()
1083    }
1084
1085    fn ready(self) -> Ready<Self>
1086    where
1087        Self: Sized,
1088    {
1089        std::future::ready(self)
1090    }
1091
1092    fn repeat(self) -> Repeat<Self>
1093    where
1094        Self: Clone,
1095    {
1096        std::iter::repeat(self)
1097    }
1098
1099    fn reversed<X, Y>(self) -> (Y, X)
1100    where
1101        Self: Is<(X, Y)>,
1102    {
1103        let (x, y) = self.into_self();
1104
1105        y.pair(x)
1106    }
1107
1108    #[cfg(feature = "async")]
1109    async fn run_for(mut self, duration: Duration) -> Result<Self, RunForError<Self::Output>>
1110    where
1111        Self: Future + Sized + Unpin,
1112    {
1113        tokio::select! {
1114            output = &mut self => RunForError::new(output).err(),
1115            () = tokio::time::sleep(duration) => self.ok(),
1116        }
1117    }
1118
1119    #[cfg(feature = "async")]
1120    async fn run_local(self) -> Self::Output
1121    where
1122        Self: Future + Sized,
1123    {
1124        LocalSet::new().run_until(self).await
1125    }
1126
1127    fn remove_file(&self) -> Result<(), IoError>
1128    where
1129        Self: AsRef<Path>,
1130    {
1131        std::fs::remove_file(self)
1132    }
1133
1134    #[cfg(feature = "socket")]
1135    async fn respond_to<T: Request<Response = Self>>(
1136        &self,
1137        mut socket: impl BorrowMut<Socket>,
1138    ) -> Result<(), AnyhowError> {
1139        socket.borrow_mut().respond::<T>(self).await
1140    }
1141
1142    // TODO-ac2072:
1143    // - add [AsRopeSlice] trait that both [Rope] and [RopeSlice<'_>] implement
1144    // - i was doing this, but it didn't work due to some use of tempoarary variables error
1145    #[cfg(feature = "ropey")]
1146    fn saturating_chunks_at_extended_grapheme<'a>(self, extended_grapheme_index: usize) -> Chunks<'a>
1147    where
1148        Self: Is<RopeSlice<'a>>,
1149    {
1150        self.saturating_chunks_at_char(extended_grapheme_index)
1151    }
1152
1153    // TODO-ac2072
1154    #[cfg(feature = "ropey")]
1155    fn saturating_chunks_at_char<'a>(self, char_index: usize) -> Chunks<'a>
1156    where
1157        Self: Is<RopeSlice<'a>>,
1158    {
1159        let rope_slice = self.into_self();
1160        let char_index = rope_slice.len_chars().min(char_index);
1161
1162        rope_slice.chunks_at_char(char_index).0
1163    }
1164
1165    // TODO-ac2072
1166    #[cfg(feature = "ropey")]
1167    fn saturating_lines_at<'a>(self, line_index: usize) -> Lines<'a>
1168    where
1169        Self: Is<RopeSlice<'a>>,
1170    {
1171        let rope_slice = self.into_self();
1172        let line_index = rope_slice.len_lines().min(line_index);
1173
1174        rope_slice.lines_at(line_index)
1175    }
1176
1177    #[cfg(feature = "tui")]
1178    fn saturating_add_or_sub_in_place_with_max(&mut self, rhs: Self, max_value: Self, add: bool)
1179    where
1180        Self: Ord + SaturatingAdd + SaturatingSub + Sized,
1181    {
1182        let value = if add {
1183            self.saturating_add(&rhs)
1184        } else {
1185            self.saturating_sub(&rhs)
1186        };
1187
1188        *self = value.min(max_value);
1189    }
1190
1191    #[cfg(feature = "async")]
1192    async fn send_to<T: Sink<Self> + Unpin>(self, mut sink: T) -> Result<(), T::Error>
1193    where
1194        Self: Sized,
1195    {
1196        sink.send(self).await
1197    }
1198
1199    #[cfg(feature = "async")]
1200    fn send_to_oneshot(self, sender: OneshotSender<Self>) -> Result<(), AnyhowError>
1201    where
1202        Self: Sized,
1203    {
1204        // NOTE: drop error variant which wraps [Self] and may not implement [StdError]
1205        sender
1206            .send(self)
1207            .ok()
1208            .context("unable to send value over oneshot channel")
1209    }
1210
1211    #[cfg(feature = "async")]
1212    fn sleep(self) -> Sleep
1213    where
1214        Self: Is<Duration>,
1215    {
1216        tokio::time::sleep(self.into_self())
1217    }
1218
1219    fn some(self) -> Option<Self>
1220    where
1221        Self: Sized,
1222    {
1223        Some(self)
1224    }
1225
1226    #[cfg(feature = "async")]
1227    fn spawn_task(self) -> JoinHandle<Self::Output>
1228    where
1229        Self: 'static + Future + Sized + Send,
1230        Self::Output: 'static + Send,
1231    {
1232        tokio::spawn(self)
1233    }
1234
1235    // TODO-4eef0b: permit reverse search
1236    fn substr_interval(&self, query: &[u8]) -> Option<(usize, usize)>
1237    where
1238        Self: AsRef<[u8]>,
1239    {
1240        let bytes = self.as_ref();
1241        let predicate = |substr| substr == query;
1242        let query_len = query.len();
1243        let begin = bytes.windows(query_len).position(predicate)?;
1244        let end = begin + query_len;
1245
1246        (begin, end).some()
1247    }
1248
1249    #[cfg(feature = "serde")]
1250    fn take_json<T: DeserializeOwned>(&mut self, index: impl Index) -> Result<T, SerdeJsonError>
1251    where
1252        Self: BorrowMut<Json>,
1253    {
1254        self.borrow_mut()
1255            .get_mut(index)
1256            .unwrap_or(&mut Json::Null)
1257            .mem_take()
1258            .into_value_from_json()
1259    }
1260
1261    #[cfg(feature = "async")]
1262    fn timeout(self, duration: Duration) -> Timeout<Self>
1263    where
1264        Self: Future + Sized,
1265    {
1266        tokio::time::timeout(duration, self)
1267    }
1268
1269    fn toggle(&mut self)
1270    where
1271        Self: BorrowMut<bool>,
1272    {
1273        let bool_value = self.borrow_mut();
1274
1275        *bool_value = !*bool_value;
1276    }
1277
1278    #[cfg(feature = "serde")]
1279    fn to_json(&self) -> Result<Json, SerdeJsonError>
1280    where
1281        Self: Serialize,
1282    {
1283        serde_json::to_value(self)
1284    }
1285
1286    #[cfg(feature = "serde")]
1287    fn to_json_byte_str(&self) -> Result<Vec<u8>, SerdeJsonError>
1288    where
1289        Self: Serialize,
1290    {
1291        serde_json::to_vec(self)
1292    }
1293
1294    #[cfg(feature = "serde")]
1295    fn to_json_object(&self, key: &str) -> Json
1296    where
1297        Self: Serialize,
1298    {
1299        serde_json::json!({key: self})
1300    }
1301
1302    #[cfg(feature = "serde")]
1303    fn to_json_str(&self) -> Result<String, SerdeJsonError>
1304    where
1305        Self: Serialize,
1306    {
1307        serde_json::to_string(self)
1308    }
1309
1310    #[cfg(feature = "rmp")]
1311    fn to_rmp_byte_str(&self) -> Result<Vec<u8>, RmpEncodeError>
1312    where
1313        Self: Serialize,
1314    {
1315        rmp_serde::to_vec(self)
1316    }
1317
1318    #[cfg(feature = "fs")]
1319    fn to_uri(&self) -> Result<String, IoError>
1320    where
1321        Self: AsRef<Utf8Path>,
1322    {
1323        "file://".cat(self.absolute_utf8()?).ok()
1324    }
1325
1326    #[cfg(feature = "serde")]
1327    fn to_value_from_json_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, SerdeJsonError>
1328    where
1329        Self: AsRef<[u8]>,
1330    {
1331        serde_json::from_slice(self.as_ref())
1332    }
1333
1334    #[cfg(feature = "serde")]
1335    fn to_value_from_yaml_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, SerdeYamlError>
1336    where
1337        Self: AsRef<[u8]>,
1338    {
1339        serde_yaml_ng::from_slice(self.as_ref())
1340    }
1341
1342    #[cfg(feature = "serde")]
1343    fn to_value_from_json_reader<T: DeserializeOwned>(self) -> Result<T, SerdeJsonError>
1344    where
1345        Self: Read + Sized,
1346    {
1347        serde_json::from_reader(self)
1348    }
1349
1350    #[cfg(feature = "rmp")]
1351    fn to_value_from_rmp_slice<'a, T: Deserialize<'a>>(&'a self) -> Result<T, RmpDecodeError>
1352    where
1353        Self: AsRef<[u8]>,
1354    {
1355        rmp_serde::from_slice(self.as_ref())
1356    }
1357
1358    #[cfg(feature = "serde")]
1359    fn to_value_from_value<T: DeserializeOwned>(&self) -> Result<T, SerdeJsonError>
1360    where
1361        Self: Serialize,
1362    {
1363        self.to_json()?.into_value_from_json()
1364    }
1365
1366    fn try_convert<T: TryFrom<Self>>(self) -> Result<T, T::Error>
1367    where
1368        Self: Sized,
1369    {
1370        self.try_into()
1371    }
1372
1373    #[cfg(feature = "async")]
1374    async fn try_join_all<T, E>(self) -> Result<T, E>
1375    where
1376        Self: IntoIterator<Item: TryFuture> + Sized,
1377        T: FromIterator<<Self::Item as TryFuture>::Ok>,
1378        E: From<<Self::Item as TryFuture>::Error>,
1379    {
1380        futures::future::try_join_all(self)
1381            .await?
1382            .into_iter()
1383            .collect::<T>()
1384            .ok()
1385    }
1386
1387    #[cfg(feature = "async")]
1388    async fn try_wait<T, E: 'static + Send + Sync>(self) -> Result<T, AnyhowError>
1389    where
1390        Self: Is<JoinHandle<Result<T, E>>>,
1391        AnyhowError: From<E>,
1392    {
1393        self.into_self().await??.ok()
1394    }
1395
1396    #[must_use]
1397    fn type_name() -> &'static str {
1398        std::any::type_name::<Self>()
1399    }
1400
1401    fn unit(&self) {}
1402
1403    async fn wait_then_unwrap_or_pending<T>(self) -> T
1404    where
1405        Self: Future<Output = Option<T>> + Sized,
1406    {
1407        match self.await {
1408            Some(value) => value,
1409            None => std::future::pending().await,
1410        }
1411    }
1412
1413    async fn unwrap_or_pending_then_wait<F: Future + Unpin>(&mut self) -> F::Output
1414    where
1415        Self: BorrowMut<Option<F>>,
1416    {
1417        if let Some(future) = self.borrow_mut().as_mut() {
1418            future.await
1419        } else {
1420            std::future::pending().await
1421        }
1422    }
1423
1424    fn with<T>(&self, value: T) -> T {
1425        value
1426    }
1427
1428    fn with_item_pushed<T>(self, item: T) -> Vec<T>
1429    where
1430        Self: Is<Vec<T>>,
1431    {
1432        let mut vec = self.into_self();
1433
1434        vec.push(item);
1435
1436        vec
1437    }
1438
1439    fn with_str_pushed(self, rhs: &str) -> String
1440    where
1441        Self: Is<String>,
1442    {
1443        let mut string = self.into_self();
1444
1445        string.push_str(rhs);
1446
1447        string
1448    }
1449
1450    #[cfg(feature = "serde")]
1451    fn write_as_json_to<T: Write>(&self, writer: T) -> Result<(), SerdeJsonError>
1452    where
1453        Self: Serialize,
1454    {
1455        serde_json::to_writer(writer, self)
1456    }
1457
1458    fn write_all_and_flush<T: AsRef<[u8]>>(&mut self, byte_str: T) -> Result<(), IoError>
1459    where
1460        Self: Write + Unpin,
1461    {
1462        self.write_all(byte_str.as_ref())?;
1463
1464        self.flush()
1465    }
1466
1467    #[cfg(feature = "async")]
1468    async fn write_all_and_flush_async<T: AsRef<[u8]>>(&mut self, byte_str: T) -> Result<(), IoError>
1469    where
1470        Self: AsyncWriteExt + Unpin,
1471    {
1472        self.write_all(byte_str.as_ref()).await?;
1473
1474        self.flush().await
1475    }
1476}
1477
1478impl<T: ?Sized> Utils for T {}