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