wasefire_cli_tools/
cmd.rs

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Helpers around `tokio::process::Command`.
16
17use std::os::unix::process::CommandExt;
18use std::process::Output;
19
20use anyhow::{Context, Result, ensure};
21use tokio::process::{Child, Command};
22
23/// Spawns a command.
24pub fn spawn(command: &mut Command) -> Result<Child> {
25    debug!("{:?}", command.as_std());
26    command.spawn().with_context(|| context(command))
27}
28
29/// Executes a command returning its error code.
30pub async fn status(command: &mut Command) -> Result<i32> {
31    let status = spawn(command)?.wait().await.with_context(|| context(command))?;
32    status.code().context("no error code")
33}
34
35/// Executes a command exiting with the same error code on error.
36pub async fn exit_status(command: &mut Command) -> Result<()> {
37    let code = status(command).await?;
38    if code != 0 {
39        std::process::exit(code);
40    }
41    Ok(())
42}
43
44/// Executes a command making sure it's successful.
45pub async fn execute(command: &mut Command) -> Result<()> {
46    let code = status(command).await?;
47    ensure!(code == 0, "failed with code {code}");
48    Ok(())
49}
50
51/// Replaces the current program with the command.
52pub fn replace(mut command: Command) -> ! {
53    debug!("{:?}", command.as_std());
54    panic!("{}", command.as_std_mut().exec());
55}
56
57/// Executes the command making sure it's successful and returns its output.
58pub async fn output(command: &mut Command) -> Result<Output> {
59    debug!("{:?}", command.as_std());
60    let output = command.output().await.with_context(|| context(command))?;
61    ensure!(output.status.success(), "failed with status {}", output.status);
62    Ok(output)
63}
64
65/// Executes the command making sure it's successful and returns exactly one line.
66pub async fn output_line(command: &mut Command) -> Result<String> {
67    let mut output = output(command).await?;
68    assert!(output.stderr.is_empty());
69    assert_eq!(output.stdout.pop(), Some(b'\n'));
70    Ok(String::from_utf8(output.stdout)?)
71}
72
73fn context(command: &Command) -> String {
74    format!("executing {:?}", command.as_std())
75}