framework_system/
command.rs1use std::process::{Child, Command, ExitStatus, Output};
7
8use anyhow::{Context, Result, anyhow};
9
10pub trait CommandExt {
17 fn command_success(&mut self) -> bool;
19
20 fn command_run(&mut self) -> Result<()>;
22
23 fn command_output(&mut self) -> Result<Output>;
25
26 fn command_status(&mut self) -> Result<ExitStatus>;
28
29 fn command_stdout(&mut self) -> Option<String>;
33
34 fn command_stderr(&mut self) -> Option<String>;
36
37 fn command_spawn(&mut self) -> Result<Child>;
39
40 fn command_line(&self) -> String;
42}
43
44impl CommandExt for Command {
45 fn command_success(&mut self) -> bool {
46 self.output().is_ok_and(|output| output.status.success())
47 }
48
49 fn command_run(&mut self) -> Result<()> {
50 let output = self.command_output()?;
51 if output.status.success() {
52 return Ok(());
53 }
54
55 Err(failure(self, &output))
56 }
57
58 fn command_output(&mut self) -> Result<Output> {
59 let program = program(self);
60
61 self.output()
62 .with_context(|| format!("执行 {program} 失败,请确认该命令存在且可用"))
63 }
64
65 fn command_status(&mut self) -> Result<ExitStatus> {
66 let program = program(self);
67
68 self.status()
69 .with_context(|| format!("执行 {program} 失败,请确认该命令存在且可用"))
70 }
71
72 fn command_stdout(&mut self) -> Option<String> {
73 Some(text(&self.command_output().ok()?.stdout))
74 }
75
76 fn command_stderr(&mut self) -> Option<String> {
77 Some(text(&self.command_output().ok()?.stderr))
78 }
79
80 fn command_spawn(&mut self) -> Result<Child> {
81 let program = program(self);
82
83 self.spawn()
84 .with_context(|| format!("启动 {program} 失败,请确认该命令存在且可用"))
85 }
86
87 fn command_line(&self) -> String {
88 let mut line = program(self);
89 for arg in self.get_args() {
90 line.push(' ');
91 line.push_str(&arg.to_string_lossy());
92 }
93
94 line
95 }
96}
97
98fn program(command: &Command) -> String {
100 command.get_program().to_string_lossy().into_owned()
101}
102
103fn failure(command: &Command, output: &Output) -> anyhow::Error {
105 let code = output
106 .status
107 .code()
108 .map_or_else(|| "未知".to_string(), |code| code.to_string());
109
110 anyhow!(
111 "{} 执行失败\n 命令:{}\n 退出码:{code}\n 标准输出:{}\n 标准错误:{}",
112 program(command),
113 command.command_line(),
114 output_text(&output.stdout),
115 output_text(&output.stderr),
116 )
117}
118
119fn text(bytes: &[u8]) -> String {
121 String::from_utf8_lossy(bytes).into_owned()
122}
123
124fn output_text(bytes: &[u8]) -> String {
126 let text = text(bytes);
127 let text = text.trim();
128 if text.is_empty() {
129 "(空)".to_string()
130 } else {
131 text.to_string()
132 }
133}