1pub mod command;
4pub mod output;
5pub mod parser;
6pub mod script;
7pub mod term;
8pub mod util;
9
10use std::path::Path;
11
12use script::{ScriptFile, ScriptOutput, ScriptRunArgs, ScriptRunError};
13
14pub struct RunError {
16 pub error: String,
17 pub output: String,
18}
19
20impl std::fmt::Display for RunError {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "{}", self.error)
23 }
24}
25
26impl std::fmt::Debug for RunError {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 write!(f, "{}", self.error)
29 }
30}
31
32fn make_args() -> ScriptRunArgs {
33 ScriptRunArgs {
34 quiet: true,
35 no_color: true,
36 simplified_output: true,
37 ..Default::default()
38 }
39}
40
41fn execute(parsed: &script::Script, output: ScriptOutput) -> Result<(), ScriptRunError> {
42 parsed.run_with_args(make_args(), output)
43}
44
45fn get_inline_file() -> ScriptFile {
46 ScriptFile::new(std::env::current_dir().unwrap().join("<inline>"))
47}
48
49pub fn run(script: &str) {
51 let file = get_inline_file();
52 let parsed =
53 parser::parse_script(file, script).unwrap_or_else(|e| panic!("clitest parse error: {e}"));
54 let output = ScriptOutput::no_color();
55 execute(&parsed, output).unwrap_or_else(|e| panic!("clitest failed: {e}"));
56}
57
58pub fn run_with_path(path: impl AsRef<Path>, script: &str) {
60 let file = ScriptFile::new(path);
61 let parsed =
62 parser::parse_script(file, script).unwrap_or_else(|e| panic!("clitest parse error: {e}"));
63 let output = ScriptOutput::no_color();
64 execute(&parsed, output).unwrap_or_else(|e| panic!("clitest failed: {e}"));
65}
66
67pub fn run_captured(script: &str) -> String {
69 match try_run_captured(script) {
70 Ok(output) => output,
71 Err(e) => panic!("clitest failed: {}\n\nOutput:\n{}", e.error, e.output),
72 }
73}
74
75pub fn run_with_path_captured(
77 name: &str,
78 line: usize,
79 path: impl AsRef<Path>,
80 script: &str,
81) -> String {
82 let file =
83 ScriptFile::new_with_line(dunce::canonicalize(path.as_ref()).unwrap().join(name), line);
84 let parsed = match parser::parse_script(file, script) {
85 Ok(s) => s,
86 Err(e) => panic!("clitest parse error: {e}"),
87 };
88 let output = ScriptOutput::quiet(true);
89 match execute(&parsed, output.clone()) {
90 Ok(()) => output.take_buffer(),
91 Err(e) => panic!("clitest failed: {e}\n\nOutput:\n{}", output.take_buffer()),
92 }
93}
94
95pub fn try_run_captured(script: &str) -> Result<String, RunError> {
98 let file = get_inline_file();
99 let parsed = match parser::parse_script(file, script) {
100 Ok(s) => s,
101 Err(e) => {
102 return Err(RunError {
103 error: e.to_string(),
104 output: String::new(),
105 });
106 }
107 };
108 let output = ScriptOutput::quiet(true);
109 match execute(&parsed, output.clone()) {
110 Ok(()) => Ok(output.take_buffer()),
111 Err(e) => Err(RunError {
112 error: e.to_string(),
113 output: output.take_buffer(),
114 }),
115 }
116}
117
118pub fn run_file(path: impl AsRef<Path>) {
120 let file = ScriptFile::new(path);
121 let parsed = parser::parse_script_file(None, file)
122 .unwrap_or_else(|e| panic!("clitest parse error: {:?}", e));
123 let output = ScriptOutput::no_color();
124 execute(&parsed, output).unwrap_or_else(|e| panic!("clitest failed: {e}"));
125}
126
127pub fn run_file_captured(path: impl AsRef<Path>) -> String {
129 match try_run_file_captured(path) {
130 Ok(output) => output,
131 Err(e) => panic!("clitest failed: {}\n\nOutput:\n{}", e.error, e.output),
132 }
133}
134
135pub fn try_run_file_captured(path: impl AsRef<Path>) -> Result<String, RunError> {
138 let file = ScriptFile::new(path);
139 let parsed = match parser::parse_script_file(None, file) {
140 Ok(s) => s,
141 Err(e) => {
142 let msg = e
143 .iter()
144 .map(|e| e.to_string())
145 .collect::<Vec<_>>()
146 .join("\n");
147 return Err(RunError {
148 error: msg,
149 output: String::new(),
150 });
151 }
152 };
153 let output = ScriptOutput::quiet(true);
154 match execute(&parsed, output.clone()) {
155 Ok(()) => Ok(output.take_buffer()),
156 Err(e) => Err(RunError {
157 error: e.to_string(),
158 output: output.take_buffer(),
159 }),
160 }
161}
162
163#[macro_export]
176macro_rules! clitest {
177 ($name:ident, $script:expr) => {
178 #[test]
179 fn $name() {
180 let output = $crate::run_with_path_captured(
181 stringify!($name),
182 line!() as _,
183 std::env::current_dir().unwrap(),
184 &format!("#!/usr/bin/env clitest --v0\n{}", $script),
185 );
186 eprintln!("{output}");
187 }
188 };
189}
190
191clitest!(
192 test_run_macro,
193 r#"
194$ echo $PWD
195*
196cd "src/parser";
197$ echo $PWD
198*
199"#
200);