unistore_process/
output.rs1use crate::child::ExitStatus;
6use crate::error::ProcessError;
7
8#[derive(Debug, Clone)]
10pub struct ProcessOutput {
11 pub status: ExitStatus,
13 pub stdout: Vec<u8>,
15 pub stderr: Vec<u8>,
17}
18
19impl ProcessOutput {
20 pub fn new(status: ExitStatus, stdout: Vec<u8>, stderr: Vec<u8>) -> Self {
22 Self {
23 status,
24 stdout,
25 stderr,
26 }
27 }
28
29 pub fn success(&self) -> bool {
31 self.status.success()
32 }
33
34 pub fn code(&self) -> Option<i32> {
36 self.status.code()
37 }
38
39 pub fn stdout_string(&self) -> Result<String, ProcessError> {
41 String::from_utf8(self.stdout.clone()).map_err(ProcessError::from)
42 }
43
44 pub fn stdout_lossy(&self) -> String {
46 String::from_utf8_lossy(&self.stdout).to_string()
47 }
48
49 pub fn stderr_string(&self) -> Result<String, ProcessError> {
51 String::from_utf8(self.stderr.clone()).map_err(ProcessError::from)
52 }
53
54 pub fn stderr_lossy(&self) -> String {
56 String::from_utf8_lossy(&self.stderr).to_string()
57 }
58
59 pub fn stdout_lines(&self) -> Vec<String> {
61 self.stdout_lossy()
62 .lines()
63 .map(|s| s.to_string())
64 .collect()
65 }
66
67 pub fn stderr_lines(&self) -> Vec<String> {
69 self.stderr_lossy()
70 .lines()
71 .map(|s| s.to_string())
72 .collect()
73 }
74
75 pub fn ensure_success(&self) -> Result<&Self, ProcessError> {
77 if self.success() {
78 Ok(self)
79 } else {
80 let stderr = self.stderr_lossy();
81 Err(ProcessError::Internal(format!(
82 "进程退出码: {:?}, stderr: {}",
83 self.code(),
84 if stderr.is_empty() { "(empty)" } else { &stderr }
85 )))
86 }
87 }
88}
89
90impl Default for ProcessOutput {
91 fn default() -> Self {
92 Self {
93 status: ExitStatus::from_code(0),
94 stdout: Vec::new(),
95 stderr: Vec::new(),
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_output_success() {
106 let output = ProcessOutput::new(
107 ExitStatus::from_code(0),
108 b"hello\nworld".to_vec(),
109 Vec::new(),
110 );
111
112 assert!(output.success());
113 assert_eq!(output.code(), Some(0));
114 assert_eq!(output.stdout_lines(), vec!["hello", "world"]);
115 }
116
117 #[test]
118 fn test_output_failure() {
119 let output = ProcessOutput::new(
120 ExitStatus::from_code(1),
121 Vec::new(),
122 b"error message".to_vec(),
123 );
124
125 assert!(!output.success());
126 assert_eq!(output.code(), Some(1));
127 assert_eq!(output.stderr_lossy(), "error message");
128 }
129
130 #[test]
131 fn test_ensure_success() {
132 let success = ProcessOutput::new(ExitStatus::from_code(0), Vec::new(), Vec::new());
133 assert!(success.ensure_success().is_ok());
134
135 let failure = ProcessOutput::new(ExitStatus::from_code(1), Vec::new(), b"err".to_vec());
136 assert!(failure.ensure_success().is_err());
137 }
138}