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