wang_utils/command/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
pub async fn execute_async(
    command: &str,
    folder: Option<&str>,
    stdout_fn: impl Fn(String) + Send + Sync + 'static,
    stderr_fn: impl Fn(String) + Send + Sync + 'static,
    complete_fn: impl Fn() + Send + Sync + 'static,
) -> anyhow::Result<bool> {
    use std::io::{BufRead, BufReader};
    use std::process::{Command, Stdio};

    std::env::set_var("IS_TTY", "true");
    let mut split = command.split(" ").collect::<Vec<&str>>();
    let mut envs = vec![];
    while split[0].contains("=") {
        let split1 = split[0].split("=").collect::<Vec<&str>>();
        let key = split1[0].trim();
        let value = split1[1].trim();
        envs.push((key, value));
        split = split.iter().skip(1).map(|r| *r).collect::<Vec<&str>>();
    }
    let args = split
        .iter()
        .skip(1)
        .map(|r| {
            if r.starts_with("\"") && r.ends_with("\"") {
                let x = &r[1..r.len() - 1];
                return x;
            }
            *r
        })
        .collect::<Vec<&str>>();
    let args = args.as_slice();
    let mut child = Command::new(split[0]);
    for (key, value) in envs {
        child.env(key, value);
    }
    let child = child.args(args);
    // .args(&["antora-playbook.yml"])

    let child = if folder.is_some() {
        let folder = folder.unwrap();
        child.current_dir(folder)
    } else {
        child
    };
    let child = child
        .stdout(Stdio::piped()) // 捕获子进程的标准输出
        .stderr(Stdio::piped()) // 捕获子进程的标准错误
        .spawn();

    if child.is_err() {
        let error = child.err().unwrap();
        stderr_fn(error.to_string());
        return Ok(false);
    }
    let mut child = child?;
    // 获取子进程的标准输出和标准错误
    let stdout = child.stdout.take().unwrap();
    let stderr = child.stderr.take().unwrap();

    // 在后台读取标准输出
    let handle1 = tokio::spawn(async move {
        let reader = BufReader::new(stdout);
        for line in reader.lines() {
            if let Ok(line) = line {
                stdout_fn(line);
            }
        }
    });
    // 在后台读取标准错误
    let handle2 = tokio::spawn(async move {
        let reader = BufReader::new(stderr);
        for line in reader.lines() {
            if let Ok(line) = line {
                stderr_fn(line);
            }
        }
    });
    tokio::try_join!(handle1, handle2)?;
    // 等待子进程结束
    let status = child.wait()?;
    // 检查子进程是否成功退出
    if status.success() {
        complete_fn();
        Ok(true)
    } else {
        Ok(false)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[tokio::test]
    async fn test_command() {
        let command = "pwd";
        let result = execute_async(
            command,
            Some("/Users/wangbin/src/docs/docs-taiwu"),
            |line| {
                println!("stdout:{line}");
            },
            |line| {
                println!("stdout:{line}");
            },
            || {
                println!("complete");
            },
        )
        .await
        .unwrap();
        println!("result:{}", result);
    }

    #[tokio::test]
    async fn test_command1() {
        let result = execute_async(
            "DOCKER_BUILDKIT=0 podman --help",
            Some("/Users/wangbin/src/docs/docs-taiwu"),
            |line| {
                println!("stdout:{line}");
            },
            |line| {
                println!("stdout:{line}");
            },
            || {
                println!("complete");
            },
        )
        .await
        .unwrap();
        println!("result:{}", result);
    }
    #[tokio::test]
    async fn test_command2() {
        let result = execute_async(
            "DOCKER_BUILDKIT=0 podman ps \"--help\"",
            Some("/Users/wangbin/src/docs/docs-taiwu"),
            |line| {
                println!("stdout:{line}");
            },
            |line| {
                println!("stdout:{line}");
            },
            || {
                println!("complete");
            },
        )
        .await
        .unwrap();
        println!("result:{}", result);
    }

    #[test]
    fn test_slice() {
        let a = "\"--help\"";
        println!("a:{a}");
        let x = &a[1..a.len() - 1];
        println!("x:{x}");
    }
}