kawauso_process/run/line.rs
1//! One line of the output of a command
2
3use std::fmt::Display;
4use std::fmt::Formatter;
5use std::fmt::Result;
6
7use super::stream::Stream;
8use super::text::Text;
9
10/// One line of the output of a command
11///
12/// A line carries the text that the command wrote and the stream that wrote
13/// it. The text holds neither the `\n` nor the `\r\n` that ended the line, so
14/// a caller can put the line into a log, a report, or an event of its own
15/// without a trim.
16///
17/// The last line of a stream can end with no such characters at all, when the
18/// command ends after its last byte. That line reaches the caller as well.
19///
20/// # Examples
21///
22/// ```
23/// use kawauso_process::run::Line;
24/// use kawauso_process::run::Stream;
25///
26/// let line = Line::new(Stream::StandardError, "no such file");
27///
28/// assert_eq!(line.text().get(), "no such file");
29/// ```
30#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
31pub struct Line {
32 /// The stream that produced the line
33 stream: Stream,
34
35 /// The text of the line, without the characters that ended it
36 text: Text,
37}
38
39impl Line {
40 /// Creates one line of the output of a command
41 ///
42 /// A run builds this value, and a caller builds one where a test stands
43 /// in for a command that no one starts.
44 pub fn new(stream: Stream, text: impl Into<Text>) -> Self {
45 Self {
46 stream,
47 text: text.into(),
48 }
49 }
50
51 /// Returns the stream that produced the line
52 ///
53 /// A caller that shows a diagnostic apart from a result, or that drops
54 /// one of the two, reads the stream here.
55 // process[impl stream.tag]
56 pub fn stream(&self) -> Stream {
57 self.stream
58 }
59
60 /// Returns the text of the line
61 pub fn text(&self) -> &Text {
62 &self.text
63 }
64}
65
66/// Shows the line for a reader
67///
68/// The text is the line alone, without the name of the stream, because a
69/// caller that wants the name has the stream and chooses how to show it.
70impl Display for Line {
71 fn fmt(&self, formatter: &mut Formatter<'_>) -> Result {
72 formatter.write_str(self.text.get())
73 }
74}
75
76#[cfg(test)]
77mod tests {
78 // An assertion in a test panics by design. A `# Panics` section on every
79 // test would repeat that and give the reader no information.
80 #![allow(clippy::missing_panics_doc)]
81
82 use super::*;
83
84 // A caller keeps a line in a report that another thread reads. This test
85 // holds the type to the auto traits that make this possible, because a
86 // private field of a later version could take them away without a word
87 // from the compiler.
88 #[test]
89 fn line_is_send_and_sync() {
90 fn assert_send_and_sync<T: Send + Sync>() {}
91
92 assert_send_and_sync::<Line>();
93 }
94
95 // The text of a line reaches a log line through `Display`, and a caller
96 // that shows one line gets the text without the name of the stream.
97 #[test]
98 fn to_string_returns_the_text_of_the_line() {
99 let line = Line::new(Stream::StandardOutput, "hello");
100
101 assert_eq!(line.to_string(), "hello");
102 }
103}