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(name: &str, path: impl AsRef<Path>, script: &str) -> String {
77 let file = ScriptFile::new(dunce::canonicalize(path.as_ref()).unwrap().join(name));
78 let parsed = match parser::parse_script(file, script) {
79 Ok(s) => s,
80 Err(e) => panic!("clitest parse error: {e}"),
81 };
82 let output = ScriptOutput::quiet(true);
83 match execute(&parsed, output.clone()) {
84 Ok(()) => output.take_buffer(),
85 Err(e) => panic!("clitest failed: {e}\n\nOutput:\n{}", output.take_buffer()),
86 }
87}
88
89pub fn try_run_captured(script: &str) -> Result<String, RunError> {
92 let file = get_inline_file();
93 let parsed = match parser::parse_script(file, script) {
94 Ok(s) => s,
95 Err(e) => {
96 return Err(RunError {
97 error: e.to_string(),
98 output: String::new(),
99 });
100 }
101 };
102 let output = ScriptOutput::quiet(true);
103 match execute(&parsed, output.clone()) {
104 Ok(()) => Ok(output.take_buffer()),
105 Err(e) => Err(RunError {
106 error: e.to_string(),
107 output: output.take_buffer(),
108 }),
109 }
110}
111
112pub fn run_file(path: impl AsRef<Path>) {
114 let file = ScriptFile::new(path);
115 let parsed = parser::parse_script_file(None, file)
116 .unwrap_or_else(|e| panic!("clitest parse error: {:?}", e));
117 let output = ScriptOutput::no_color();
118 execute(&parsed, output).unwrap_or_else(|e| panic!("clitest failed: {e}"));
119}
120
121pub fn run_file_captured(path: impl AsRef<Path>) -> String {
123 match try_run_file_captured(path) {
124 Ok(output) => output,
125 Err(e) => panic!("clitest failed: {}\n\nOutput:\n{}", e.error, e.output),
126 }
127}
128
129pub fn try_run_file_captured(path: impl AsRef<Path>) -> Result<String, RunError> {
132 let file = ScriptFile::new(path);
133 let parsed = match parser::parse_script_file(None, file) {
134 Ok(s) => s,
135 Err(e) => {
136 let msg = e
137 .iter()
138 .map(|e| e.to_string())
139 .collect::<Vec<_>>()
140 .join("\n");
141 return Err(RunError {
142 error: msg,
143 output: String::new(),
144 });
145 }
146 };
147 let output = ScriptOutput::quiet(true);
148 match execute(&parsed, output.clone()) {
149 Ok(()) => Ok(output.take_buffer()),
150 Err(e) => Err(RunError {
151 error: e.to_string(),
152 output: output.take_buffer(),
153 }),
154 }
155}
156
157#[macro_export]
170macro_rules! clitest {
171 ($name:ident, $script:expr) => {
172 #[test]
173 fn $name() {
174 let output = $crate::run_with_path_captured(stringify!($name), std::env::current_dir().unwrap(), &format!("#!/usr/bin/env clitest --v0\n{}", $script));
175 eprintln!("{output}");
176 }
177 };
178}
179
180clitest!(test_run_macro, r#"
181$ echo $PWD
182*
183"#);