timeout_readwrite/
reader.rs1use nix::libc::c_int;
10use nix::poll::PollFlags;
11use std::io::Read;
12use std::io::Result;
13use std::io::Seek;
14use std::io::SeekFrom;
15use std::os::fd::AsFd;
16use std::time::Duration;
17
18use super::utils;
19
20pub struct TimeoutReader<H>
31where
32 H: Read + AsFd,
33{
34 timeout: Option<c_int>,
35 handle: H,
36}
37
38impl<H> Read for TimeoutReader<H>
39where
40 H: Read + AsFd,
41{
42 fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
43 utils::wait_until_ready(self.timeout, &self.handle, PollFlags::POLLIN)?;
44 self.handle.read(buf)
45 }
46}
47
48impl<H> Seek for TimeoutReader<H>
49where
50 H: Read + AsFd + Seek,
51{
52 fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
53 self.handle.seek(pos)
54 }
55}
56
57impl<H> AsFd for TimeoutReader<H>
58where
59 H: Read + AsFd,
60{
61 fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> {
62 self.handle.as_fd()
63 }
64}
65
66impl<H> Clone for TimeoutReader<H>
67where
68 H: Read + AsFd + Clone,
69{
70 fn clone(&self) -> TimeoutReader<H> {
71 TimeoutReader {
72 handle: self.handle.clone(),
73 ..*self
74 }
75 }
76}
77
78impl<H> TimeoutReader<H>
79where
80 H: Read + AsFd,
81{
82 pub fn new<T: Into<Option<Duration>>>(handle: H, timeout: T) -> TimeoutReader<H> {
114 TimeoutReader {
115 timeout: timeout.into().map(utils::duration_to_ms),
116 handle,
117 }
118 }
119}
120
121pub trait TimeoutReadExt<H>
122where
123 H: Read + AsFd,
124{
125 fn with_timeout<T: Into<Option<Duration>>>(self, timeout: T) -> TimeoutReader<H>;
126}
127
128impl<H> TimeoutReadExt<H> for H
129where
130 H: Read + AsFd,
131{
132 fn with_timeout<T: Into<Option<Duration>>>(self, timeout: T) -> TimeoutReader<H> {
133 TimeoutReader::new(self, timeout)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use std::env;
140 use std::fs::File;
141 use std::io::Read;
142 use std::os::fd::AsRawFd;
143 use std::path::PathBuf;
144 use std::time::Duration;
145
146 use super::*;
147
148 lazy_static! {
149 static ref CRATE_ROOT: PathBuf = {
150 env::current_exe()
151 .unwrap()
152 .parent()
153 .unwrap()
154 .parent()
155 .unwrap()
156 .parent()
157 .unwrap()
158 .parent()
159 .unwrap()
160 .to_path_buf()
161 };
162 }
163
164 #[test]
165 fn read_regular_file_with_timeout() {
166 let original_contents = include_str!("../test_data/regular_file.txt");
167
168 let mut regular_file = CRATE_ROOT.clone();
169 regular_file.push("test_data");
170 regular_file.push("regular_file.txt");
171
172 let fp = File::open(regular_file).unwrap();
173 let fp_fd = fp.as_raw_fd();
174 let mut fp = TimeoutReader::new(fp, Duration::new(5, 0));
175
176 let mut read_contents = String::new();
177 fp.read_to_string(&mut read_contents).unwrap();
178
179 assert_eq!(original_contents, read_contents);
180
181 assert_eq!(fp_fd, fp.as_fd().as_raw_fd());
182 }
183
184 #[test]
185 fn read_regular_file_no_timeout() {
186 let original_contents = include_str!("../test_data/regular_file.txt");
187
188 let mut regular_file = CRATE_ROOT.clone();
189 regular_file.push("test_data");
190 regular_file.push("regular_file.txt");
191
192 let fp = File::open(regular_file).unwrap();
193 let mut fp = TimeoutReader::new(fp, None);
194
195 let mut read_contents = String::new();
196 fp.read_to_string(&mut read_contents).unwrap();
197
198 assert_eq!(original_contents, read_contents);
199 }
200
201 #[test]
202 fn read_regular_file_with_timeout_extension_trait() {
203 let original_contents = include_str!("../test_data/regular_file.txt");
204
205 let mut regular_file = CRATE_ROOT.clone();
206 regular_file.push("test_data");
207 regular_file.push("regular_file.txt");
208
209 let fp = File::open(regular_file).unwrap();
210 let mut fp = fp.with_timeout(Duration::new(5, 0));
211
212 let mut read_contents = String::new();
213 fp.read_to_string(&mut read_contents).unwrap();
214
215 assert_eq!(original_contents, read_contents);
216 }
217}