Skip to main content

command_stream/
result_streams.rs

1//! Re-readable snapshots of completed command output and a writable stdin record.
2
3use serde::{Serialize, Serializer};
4use std::fmt;
5use std::io::{self, Read, Write};
6use std::ops::Deref;
7use std::path::Path;
8
9/// Captured text that also implements `Read`. Reading advances a byte cursor;
10/// string methods keep working on the complete, unconsumed snapshot.
11#[derive(Debug, Clone, Default)]
12pub struct CapturedOutput {
13    text: String,
14    position: usize,
15}
16
17impl CapturedOutput {
18    pub fn new(text: impl Into<String>) -> Self {
19        Self {
20            text: text.into(),
21            position: 0,
22        }
23    }
24
25    pub fn rewind(&mut self) {
26        self.position = 0;
27    }
28}
29
30impl From<String> for CapturedOutput {
31    fn from(text: String) -> Self {
32        Self::new(text)
33    }
34}
35
36impl From<&str> for CapturedOutput {
37    fn from(text: &str) -> Self {
38        Self::new(text)
39    }
40}
41
42impl Deref for CapturedOutput {
43    type Target = String;
44    fn deref(&self) -> &String {
45        &self.text
46    }
47}
48
49impl AsRef<Path> for CapturedOutput {
50    fn as_ref(&self) -> &Path {
51        Path::new(&self.text)
52    }
53}
54
55impl AsRef<str> for CapturedOutput {
56    fn as_ref(&self) -> &str {
57        &self.text
58    }
59}
60
61impl AsRef<[u8]> for CapturedOutput {
62    fn as_ref(&self) -> &[u8] {
63        self.text.as_bytes()
64    }
65}
66
67impl Read for CapturedOutput {
68    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
69        let bytes = self.text.as_bytes();
70        let remaining = &bytes[self.position..];
71        let size = buf.len().min(remaining.len());
72        buf[..size].copy_from_slice(&remaining[..size]);
73        self.position += size;
74        Ok(size)
75    }
76}
77
78impl fmt::Display for CapturedOutput {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        self.text.fmt(f)
81    }
82}
83
84impl Serialize for CapturedOutput {
85    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
86        serializer.serialize_str(&self.text)
87    }
88}
89
90impl PartialEq<str> for CapturedOutput {
91    fn eq(&self, other: &str) -> bool {
92        self.text == other
93    }
94}
95
96impl PartialEq<&str> for CapturedOutput {
97    fn eq(&self, other: &&str) -> bool {
98        self.text == *other
99    }
100}
101
102impl PartialEq<String> for CapturedOutput {
103    fn eq(&self, other: &String) -> bool {
104        &self.text == other
105    }
106}
107
108impl PartialEq for CapturedOutput {
109    fn eq(&self, other: &Self) -> bool {
110        self.text == other.text
111    }
112}
113
114impl Eq for CapturedOutput {}
115
116/// Writable record of input sent to a completed command. Writes after completion
117/// change this record only; use `ProcessRunner::write_stdin` for live input.
118#[derive(Debug, Clone, Default)]
119pub struct CapturedInput(Vec<u8>);
120
121impl CapturedInput {
122    pub fn new(bytes: impl Into<Vec<u8>>) -> Self {
123        Self(bytes.into())
124    }
125    pub fn as_bytes(&self) -> &[u8] {
126        &self.0
127    }
128}
129
130impl Write for CapturedInput {
131    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
132        self.0.extend_from_slice(buf);
133        Ok(buf.len())
134    }
135    fn flush(&mut self) -> io::Result<()> {
136        Ok(())
137    }
138}
139
140impl fmt::Display for CapturedInput {
141    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142        String::from_utf8_lossy(&self.0).fmt(f)
143    }
144}