git_features/
interrupt.rs

1//! Utilities to cause interruptions in common traits, like Read/Write and Iterator.
2use std::{
3    io,
4    sync::atomic::{AtomicBool, Ordering},
5};
6
7/// A wrapper for an inner iterator which will check for interruptions on each iteration, stopping the iteration when
8/// that is requested.
9pub struct Iter<'a, I> {
10    /// The actual iterator to yield elements from.
11    pub inner: I,
12    should_interrupt: &'a AtomicBool,
13}
14
15impl<'a, I> Iter<'a, I>
16where
17    I: Iterator,
18{
19    /// Create a new iterator over `inner` which checks for interruptions on each iteration on `should_interrupt`.
20    ///
21    /// Note that this means the consumer of the iterator data should also be able to access `should_interrupt` and
22    /// consider it when producing the final result to avoid claiming success even though the operation is only partially
23    /// complete.
24    pub fn new(inner: I, should_interrupt: &'a AtomicBool) -> Self {
25        Iter {
26            inner,
27            should_interrupt,
28        }
29    }
30}
31
32impl<'a, I> Iterator for Iter<'a, I>
33where
34    I: Iterator,
35{
36    type Item = I::Item;
37
38    fn next(&mut self) -> Option<Self::Item> {
39        if self.should_interrupt.load(Ordering::Relaxed) {
40            return None;
41        }
42        self.inner.next()
43    }
44}
45
46/// A wrapper for an inner iterator which will check for interruptions on each iteration.
47pub struct IterWithErr<'a, I, EFN> {
48    /// The actual iterator to yield elements from.
49    pub inner: I,
50    make_err: Option<EFN>,
51    should_interrupt: &'a AtomicBool,
52}
53
54impl<'a, I, EFN, E> IterWithErr<'a, I, EFN>
55where
56    I: Iterator,
57    EFN: FnOnce() -> E,
58{
59    /// Create a new iterator over `inner` which checks for interruptions on each iteration and calls `make_err()` to
60    /// signal an interruption happened, causing no further items to be iterated from that point on.
61    pub fn new(inner: I, make_err: EFN, should_interrupt: &'a AtomicBool) -> Self {
62        IterWithErr {
63            inner,
64            make_err: Some(make_err),
65            should_interrupt,
66        }
67    }
68}
69
70impl<'a, I, EFN, E> Iterator for IterWithErr<'a, I, EFN>
71where
72    I: Iterator,
73    EFN: FnOnce() -> E,
74{
75    type Item = Result<I::Item, E>;
76
77    fn next(&mut self) -> Option<Self::Item> {
78        self.make_err.as_ref()?;
79        if self.should_interrupt.load(Ordering::Relaxed) {
80            return self.make_err.take().map(|f| Err(f()));
81        }
82        match self.inner.next() {
83            Some(next) => Some(Ok(next)),
84            None => {
85                self.make_err = None;
86                None
87            }
88        }
89    }
90}
91
92/// A wrapper for implementors of [`std::io::Read`] or [`std::io::BufRead`] with interrupt support.
93///
94/// It fails a [read][`std::io::Read::read`] while an interrupt was requested.
95pub struct Read<'a, R> {
96    /// The actual implementor of [`std::io::Read`] to which interrupt support will be added.
97    pub inner: R,
98    /// The flag to trigger interruption
99    pub should_interrupt: &'a AtomicBool,
100}
101
102impl<'a, R> io::Read for Read<'a, R>
103where
104    R: io::Read,
105{
106    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
107        if self.should_interrupt.load(Ordering::Relaxed) {
108            return Err(std::io::Error::new(std::io::ErrorKind::Other, "Interrupted"));
109        }
110        self.inner.read(buf)
111    }
112}
113
114impl<'a, R> io::BufRead for Read<'a, R>
115where
116    R: io::BufRead,
117{
118    fn fill_buf(&mut self) -> io::Result<&[u8]> {
119        self.inner.fill_buf()
120    }
121
122    fn consume(&mut self, amt: usize) {
123        self.inner.consume(amt)
124    }
125}