pistonite_cu/process/pio/
read.rs1use std::process::Stdio;
2
3use tokio::io::{AsyncBufRead, AsyncBufReadExt as _, AsyncReadExt as _};
4use tokio::process::{Child as TokioChild, ChildStderr, ChildStdout, Command as TokioCommand};
5
6use crate::{BoxedFuture, Context as _, co};
7
8use super::{ChildOutConfig, ChildOutTask};
9
10#[inline(always)]
30pub fn buffer() -> Buffer {
31 Buffer
32}
33pub struct Buffer;
34impl ChildOutConfig for Buffer {
35 type Task = BufferTask;
36 type __Null = super::__OCNonNull;
37 fn configure_stdout(&mut self, command: &mut TokioCommand) {
38 command.stdout(Stdio::piped());
39 }
40 fn configure_stderr(&mut self, command: &mut TokioCommand) {
41 command.stderr(Stdio::piped());
42 }
43 fn take(
44 self,
45 child: &mut TokioChild,
46 _: Option<&str>,
47 is_out: bool,
48 ) -> crate::Result<Self::Task> {
49 Ok(BufferTask(super::take_child_out(child, is_out)?))
50 }
51}
52
53pub struct BufferTask(Result<ChildStdout, ChildStderr>);
54impl ChildOutTask for BufferTask {
55 type Output = co::Handle<crate::Result<Vec<u8>>>;
56
57 fn run(self) -> (Option<BoxedFuture<()>>, Self::Output) {
58 let (send, recv) = tokio::sync::oneshot::channel();
64 let output: Self::Output =
65 co::spawn(async move { recv.await.context("failed to receive output as bytes")? });
66 let task = async move {
67 let buf = read_to_end(self.0).await;
68 let _: Result<_, _> = send.send(buf);
69 };
70 (Some(Box::pin(task)), output)
71 }
72}
73
74#[inline(always)]
94pub fn string() -> BufferString {
95 BufferString
96}
97pub struct BufferString;
98impl ChildOutConfig for BufferString {
99 type Task = BufferStringTask;
100 type __Null = super::__OCNonNull;
101 fn configure_stdout(&mut self, command: &mut TokioCommand) {
102 command.stdout(Stdio::piped());
103 }
104 fn configure_stderr(&mut self, command: &mut TokioCommand) {
105 command.stderr(Stdio::piped());
106 }
107 fn take(
108 self,
109 child: &mut TokioChild,
110 _: Option<&str>,
111 is_out: bool,
112 ) -> crate::Result<Self::Task> {
113 Ok(BufferStringTask(super::take_child_out(child, is_out)?))
114 }
115}
116
117pub struct BufferStringTask(Result<ChildStdout, ChildStderr>);
118impl ChildOutTask for BufferStringTask {
119 type Output = co::Handle<crate::Result<String>>;
120
121 fn run(self) -> (Option<BoxedFuture<()>>, Self::Output) {
122 let (send, recv) = tokio::sync::oneshot::channel();
128 let output: Self::Output =
129 co::spawn(async move { recv.await.context("failed to receive output as bytes")? });
130 let task = async move {
131 let string = read_to_end(self.0).await.and_then(|buf| {
132 String::from_utf8(buf).context("failed to decode child output as utf-8")
133 });
134 let _: Result<_, _> = send.send(string);
135 };
136 (Some(Box::pin(task)), output)
137 }
138}
139
140async fn read_to_end(r: Result<ChildStdout, ChildStderr>) -> crate::Result<Vec<u8>> {
141 let mut buf = Vec::new();
142 match r {
143 Ok(mut r) => r.read_to_end(&mut buf).await,
144 Err(mut r) => r.read_to_end(&mut buf).await,
145 }
146 .context("io error while reading output")?;
147 Ok(buf)
148}
149
150#[inline(always)]
187pub fn lines() -> Lines {
188 Lines
189}
190pub struct Lines;
191impl ChildOutConfig for Lines {
192 type Task = LinesTask;
193 type __Null = super::__OCNonNull;
194 fn configure_stdout(&mut self, command: &mut TokioCommand) {
195 command.stdout(Stdio::piped());
196 }
197 fn configure_stderr(&mut self, command: &mut TokioCommand) {
198 command.stderr(Stdio::piped());
199 }
200 fn take(
201 self,
202 child: &mut TokioChild,
203 _: Option<&str>,
204 is_out: bool,
205 ) -> crate::Result<Self::Task> {
206 Ok(LinesTask(super::take_child_out(child, is_out)?))
207 }
208}
209
210pub struct LinesTask(Result<ChildStdout, ChildStderr>);
211impl ChildOutTask for LinesTask {
212 type Output = LinesOutput;
213
214 fn run(self) -> (Option<BoxedFuture<()>>, Self::Output) {
215 let (send, recv) = std::sync::mpsc::channel();
216 let output = LinesOutput { recv, done: false };
217 (Some(Box::pin(self.main(send))), output)
218 }
219}
220
221impl LinesTask {
222 async fn main(self, send: std::sync::mpsc::Sender<Option<crate::Result<String>>>) {
223 match self.0 {
224 Ok(r) => read_send_line(tokio::io::BufReader::new(r), send).await,
225 Err(r) => read_send_line(tokio::io::BufReader::new(r), send).await,
226 }
227 }
228}
229
230pub struct LinesOutput {
234 recv: std::sync::mpsc::Receiver<Option<crate::Result<String>>>,
235 done: bool,
236}
237impl Iterator for LinesOutput {
238 type Item = crate::Result<String>;
239
240 fn next(&mut self) -> Option<Self::Item> {
241 if self.done {
242 return None;
243 }
244 match self.recv.recv() {
245 Ok(Some(x)) => {
246 if x.is_err() {
247 self.done = true;
248 }
249 Some(x)
250 }
251 _ => {
253 self.done = true;
254 None
255 }
256 }
257 }
258}
259
260#[inline(always)]
294pub fn co_lines() -> CoLines {
295 CoLines
296}
297pub struct CoLines;
298impl ChildOutConfig for CoLines {
299 type Task = CoLinesTask;
300 type __Null = super::__OCNonNull;
301 fn configure_stdout(&mut self, command: &mut TokioCommand) {
302 command.stdout(Stdio::piped());
303 }
304 fn configure_stderr(&mut self, command: &mut TokioCommand) {
305 command.stderr(Stdio::piped());
306 }
307 fn take(
308 self,
309 child: &mut TokioChild,
310 _: Option<&str>,
311 is_out: bool,
312 ) -> crate::Result<Self::Task> {
313 Ok(CoLinesTask(super::take_child_out(child, is_out)?))
314 }
315}
316pub struct CoLinesTask(Result<ChildStdout, ChildStderr>);
317impl ChildOutTask for CoLinesTask {
318 type Output = CoLinesOutput;
319
320 fn run(self) -> (Option<BoxedFuture<()>>, Self::Output) {
321 let (send, recv) = tokio::sync::mpsc::unbounded_channel();
322 let output = CoLinesOutput { recv, done: false };
323 (Some(Box::pin(self.main(send))), output)
324 }
325}
326
327impl CoLinesTask {
328 async fn main(self, send: tokio::sync::mpsc::UnboundedSender<Option<crate::Result<String>>>) {
329 match self.0 {
330 Ok(r) => read_send_line(tokio::io::BufReader::new(r), send).await,
331 Err(r) => read_send_line(tokio::io::BufReader::new(r), send).await,
332 }
333 }
334}
335
336pub struct CoLinesOutput {
340 recv: tokio::sync::mpsc::UnboundedReceiver<Option<crate::Result<String>>>,
341 done: bool,
342}
343impl CoLinesOutput {
344 pub async fn next(&mut self) -> Option<crate::Result<String>> {
346 if self.done {
347 return None;
348 }
349 match self.recv.recv().await {
350 Some(Some(x)) => {
351 if x.is_err() {
352 self.done = true;
353 }
354 Some(x)
355 }
356 _ => {
358 self.done = true;
359 None
360 }
361 }
362 }
363}
364
365trait LineSender {
366 fn send(&self, payload: Option<crate::Result<String>>) -> bool;
367}
368impl LineSender for std::sync::mpsc::Sender<Option<crate::Result<String>>> {
369 fn send(&self, payload: Option<crate::Result<String>>) -> bool {
370 self.send(payload).is_ok()
371 }
372}
373impl LineSender for tokio::sync::mpsc::UnboundedSender<Option<crate::Result<String>>> {
374 fn send(&self, payload: Option<crate::Result<String>>) -> bool {
375 self.send(payload).is_ok()
376 }
377}
378async fn read_send_line<R: AsyncBufRead + Unpin, S: LineSender>(r: R, send: S) {
379 let mut lines = r.lines();
380 loop {
381 match lines.next_line().await {
382 Err(e) => {
383 let payload = Err(e).context("error reading line from child output");
385 send.send(Some(payload));
386 return;
387 }
388 Ok(Some(x)) => {
389 if !send.send(Some(Ok(x))) {
390 send.send(None);
392 return;
393 }
394 }
395 Ok(None) => {
396 let _ = send.send(None);
398 return;
399 }
400 }
401 }
402}