summaryrefslogtreecommitdiffstats
path: root/vendor/gix-features/src/interrupt.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-features/src/interrupt.rs')
-rw-r--r--vendor/gix-features/src/interrupt.rs125
1 files changed, 125 insertions, 0 deletions
diff --git a/vendor/gix-features/src/interrupt.rs b/vendor/gix-features/src/interrupt.rs
new file mode 100644
index 000000000..1f78e613a
--- /dev/null
+++ b/vendor/gix-features/src/interrupt.rs
@@ -0,0 +1,125 @@
+//! Utilities to cause interruptions in common traits, like Read/Write and Iterator.
+use std::{
+ io,
+ sync::atomic::{AtomicBool, Ordering},
+};
+
+/// A wrapper for an inner iterator which will check for interruptions on each iteration, stopping the iteration when
+/// that is requested.
+pub struct Iter<'a, I> {
+ /// The actual iterator to yield elements from.
+ pub inner: I,
+ should_interrupt: &'a AtomicBool,
+}
+
+impl<'a, I> Iter<'a, I>
+where
+ I: Iterator,
+{
+ /// Create a new iterator over `inner` which checks for interruptions on each iteration on `should_interrupt`.
+ ///
+ /// Note that this means the consumer of the iterator data should also be able to access `should_interrupt` and
+ /// consider it when producing the final result to avoid claiming success even though the operation is only partially
+ /// complete.
+ pub fn new(inner: I, should_interrupt: &'a AtomicBool) -> Self {
+ Iter {
+ inner,
+ should_interrupt,
+ }
+ }
+}
+
+impl<'a, I> Iterator for Iter<'a, I>
+where
+ I: Iterator,
+{
+ type Item = I::Item;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.should_interrupt.load(Ordering::Relaxed) {
+ return None;
+ }
+ self.inner.next()
+ }
+}
+
+/// A wrapper for an inner iterator which will check for interruptions on each iteration.
+pub struct IterWithErr<'a, I, EFN> {
+ /// The actual iterator to yield elements from.
+ pub inner: I,
+ make_err: Option<EFN>,
+ should_interrupt: &'a AtomicBool,
+}
+
+impl<'a, I, EFN, E> IterWithErr<'a, I, EFN>
+where
+ I: Iterator,
+ EFN: FnOnce() -> E,
+{
+ /// Create a new iterator over `inner` which checks for interruptions on each iteration and calls `make_err()` to
+ /// signal an interruption happened, causing no further items to be iterated from that point on.
+ pub fn new(inner: I, make_err: EFN, should_interrupt: &'a AtomicBool) -> Self {
+ IterWithErr {
+ inner,
+ make_err: Some(make_err),
+ should_interrupt,
+ }
+ }
+}
+
+impl<'a, I, EFN, E> Iterator for IterWithErr<'a, I, EFN>
+where
+ I: Iterator,
+ EFN: FnOnce() -> E,
+{
+ type Item = Result<I::Item, E>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.make_err.as_ref()?;
+ if self.should_interrupt.load(Ordering::Relaxed) {
+ return self.make_err.take().map(|f| Err(f()));
+ }
+ match self.inner.next() {
+ Some(next) => Some(Ok(next)),
+ None => {
+ self.make_err = None;
+ None
+ }
+ }
+ }
+}
+
+/// A wrapper for implementors of [`std::io::Read`] or [`std::io::BufRead`] with interrupt support.
+///
+/// It fails a [read][`std::io::Read::read`] while an interrupt was requested.
+pub struct Read<'a, R> {
+ /// The actual implementor of [`std::io::Read`] to which interrupt support will be added.
+ pub inner: R,
+ /// The flag to trigger interruption
+ pub should_interrupt: &'a AtomicBool,
+}
+
+impl<'a, R> io::Read for Read<'a, R>
+where
+ R: io::Read,
+{
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ if self.should_interrupt.load(Ordering::Relaxed) {
+ return Err(std::io::Error::new(std::io::ErrorKind::Other, "Interrupted"));
+ }
+ self.inner.read(buf)
+ }
+}
+
+impl<'a, R> io::BufRead for Read<'a, R>
+where
+ R: io::BufRead,
+{
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ self.inner.fill_buf()
+ }
+
+ fn consume(&mut self, amt: usize) {
+ self.inner.consume(amt)
+ }
+}