Skip to main content

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}