unistore_process/
output.rs

1//! 进程输出
2//!
3//! 职责:封装进程执行结果
4
5use crate::child::ExitStatus;
6use crate::error::ProcessError;
7
8/// 进程输出
9#[derive(Debug, Clone)]
10pub struct ProcessOutput {
11    /// 退出状态
12    pub status: ExitStatus,
13    /// 标准输出
14    pub stdout: Vec<u8>,
15    /// 标准错误
16    pub stderr: Vec<u8>,
17}
18
19impl ProcessOutput {
20    /// 创建新的输出
21    pub fn new(status: ExitStatus, stdout: Vec<u8>, stderr: Vec<u8>) -> Self {
22        Self {
23            status,
24            stdout,
25            stderr,
26        }
27    }
28
29    /// 判断是否成功(退出码为 0)
30    pub fn success(&self) -> bool {
31        self.status.success()
32    }
33
34    /// 获取退出码
35    pub fn code(&self) -> Option<i32> {
36        self.status.code()
37    }
38
39    /// 获取标准输出(UTF-8 字符串)
40    pub fn stdout_string(&self) -> Result<String, ProcessError> {
41        String::from_utf8(self.stdout.clone()).map_err(ProcessError::from)
42    }
43
44    /// 获取标准输出(UTF-8 字符串,有损转换)
45    pub fn stdout_lossy(&self) -> String {
46        String::from_utf8_lossy(&self.stdout).to_string()
47    }
48
49    /// 获取标准错误(UTF-8 字符串)
50    pub fn stderr_string(&self) -> Result<String, ProcessError> {
51        String::from_utf8(self.stderr.clone()).map_err(ProcessError::from)
52    }
53
54    /// 获取标准错误(UTF-8 字符串,有损转换)
55    pub fn stderr_lossy(&self) -> String {
56        String::from_utf8_lossy(&self.stderr).to_string()
57    }
58
59    /// 获取标准输出的行
60    pub fn stdout_lines(&self) -> Vec<String> {
61        self.stdout_lossy()
62            .lines()
63            .map(|s| s.to_string())
64            .collect()
65    }
66
67    /// 获取标准错误的行
68    pub fn stderr_lines(&self) -> Vec<String> {
69        self.stderr_lossy()
70            .lines()
71            .map(|s| s.to_string())
72            .collect()
73    }
74
75    /// 检查是否成功,失败则返回错误
76    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}