1pub mod preview;
2pub mod utils;
3
4use std::{
5 env, io::Write, process::{Child, Command, Stdio}, sync::LazyLock
6};
7
8use log::error;
9
10pub fn spawn(
12 cmd: &str,
13 vars: impl IntoIterator<Item = (String, String)>,
14 stdin: Stdio,
15 stdout: Stdio,
16 stderr: Stdio,
17) -> Option<Child> {
18 let (shell, arg) = &*SHELL;
19
20 Command::new(shell)
21 .arg(arg)
22 .arg(cmd)
23 .envs(vars)
24 .stdin(stdin)
25 .stdout(stdout)
26 .stderr(stderr)
27 .spawn()
28 .map_err(|e| error!("Failed to spawn command {cmd}: {e}"))
29 .ok()
30}
31
32pub fn exec(cmd: &str, vars: impl IntoIterator<Item = (String, String)>) -> ! {
33 let (shell, arg) = &*SHELL;
34
35 let mut command = Command::new(shell);
36 command.arg(arg).arg(cmd).envs(vars);
37
38 #[cfg(not(windows))]
39 {
40 use std::os::unix::process::CommandExt;
43 #[allow(irrefutable_let_patterns)]
44 if let err = command.exec() {
45 use std::process::exit;
46
47 eprintln!("Could not exec {cmd:?}: {err}");
48 exit(1);
49 }
50 }
51
52 #[cfg(windows)]
53 {
54 match command.status() {
55 Ok(status) => {
56 exit(status.code().unwrap_or(if status.success() { 0 } else { 1 }));
57 }
58 Err(err) => {
59 eprintln!("Could not spawn {cmd:?}: {err}");
60 exit(1);
61 }
62 }
63 }
64
65 unreachable!(); }
67
68
69
70pub fn tty_or_null() -> Stdio {
71 if let Ok(mut tty) = std::fs::File::open("/dev/tty") {
72 let _ = tty.flush(); Stdio::from(tty)
74 } else {
75 error!("Failed to open /dev/tty");
76 Stdio::inherit()
77 }
78}
79
80static SHELL: LazyLock<(String, String)> = LazyLock::new(|| {
81 #[cfg(windows)]
82 {
83 let path = env::var("COMSPEC").unwrap_or_else(|_| "cmd.exe".to_string());
84 let flag = if path.to_lowercase().contains("powershell") {
85 "-Command".to_string()
86 } else {
87 "/C".to_string()
88 };
89 (path, flag)
90 }
91 #[cfg(unix)]
92 {
93 use log::debug;
94
95 let path = env::var("SHELL").unwrap_or_else(|_| "/bin/sh".to_string());
96 let flag = "-c".to_string();
97 debug!("SHELL: {}, {}", path, flag);
98 (path, flag)
99 }
100});
101
102
103
104pub type EnvVars = Vec<(String, String)>;
105
106#[macro_export]
107macro_rules! env_vars {
108 ($( $name:expr => $value:expr ),* $(,)?) => {
109 Vec::<(String, String)>::from([
110 $( ($name.into(), $value.into()) ),*
111 ]
112 )
113 };
114}
115
116use std::ops::Deref;
118use std::sync::{Arc, RwLock};
119
120#[derive(Debug, Clone)]
121pub struct AppendOnly<T>(Arc<RwLock<boxcar::Vec<T>>>);
122
123impl<T> AppendOnly<T> {
124 pub fn new() -> Self {
125 Self(Arc::new(RwLock::new(boxcar::Vec::new())))
126 }
127
128 pub fn clear(&self) {
129 let mut guard = self.0.write().unwrap(); guard.clear();
131 }
132
133 pub fn push(&self, val: T) {
134 let guard = self.0.read().unwrap();
135 guard.push(val);
136 }
137
138 pub fn map_to_vec<U, F>(&self, mut f: F) -> Vec<U>
139 where
140 F: FnMut(&T) -> U,
141 {
142 let guard = self.0.read().unwrap();
143 guard.iter().map(move |(_i, v)| f(v)).collect()
144 }
145}
146
147impl<T> Deref for AppendOnly<T> {
148 type Target = RwLock<boxcar::Vec<T>>;
149
150 fn deref(&self) -> &Self::Target {
151 &self.0
152 }
153}