1#[cfg(feature = "clap")] pub mod clap;
6
7use std::path::{Path, PathBuf};
8use std::collections::HashMap;
9use std::fmt;
10use tempfile::NamedTempFile;
11
12const DEFAULT_MAX_OUTPUT_CONTEXT_LINE_COUNT: usize = 10;
13
14#[derive(Clone, Debug)]
16pub struct Config
17{
18 pub supported_file_extensions: Vec<String>,
20 pub test_paths: Vec<PathBuf>,
22 pub constants: HashMap<String, String>,
24 pub variable_lookup: VariableLookup,
31 pub cleanup_temporary_files: bool,
37 pub save_artifacts_to_directory: Option<PathBuf>,
39 pub dump_variable_resolution: bool,
41 pub truncate_output_context_to_number_of_lines: Option<usize>,
44 pub extra_executable_search_paths: Vec<PathBuf>,
47 pub always_show_stderr: bool,
50 pub shell: String,
52}
53
54#[derive(Clone)]
56pub struct VariableLookup(fn(&str) -> Option<String>);
57
58impl Config
59{
60 pub const DEFAULT_VARIABLE_LOOKUP: VariableLookup = VariableLookup(|v| {
67 if v.contains("tempfile") {
68 let temp_file = NamedTempFile::new().expect("failed to create a temporary file");
69 Some(temp_file.into_temp_path().to_str().expect("temp file path is not utf-8").to_owned())
70 } else {
71 None
72 }
73 });
74
75 pub fn add_extension<S>(&mut self, ext: S) where S: AsRef<str> {
80 self.supported_file_extensions.push(ext.as_ref().to_owned())
81 }
82
83 pub fn add_extensions(&mut self, extensions: &[&str]) {
85 self.supported_file_extensions.extend(extensions.iter().map(|s| s.to_string()));
86 }
87
88 pub fn add_search_path<P>(&mut self, path: P) where P: Into<String> {
92 self.test_paths.push(PathBuf::from(path.into()).canonicalize().unwrap());
93 }
94
95 pub fn add_executable_search_path<P>(&mut self, path: P) where P: AsRef<Path> {
97 self.extra_executable_search_paths.push(path.as_ref().to_owned())
98 }
99
100 pub fn test_search_directories(&self) -> impl Iterator<Item=&Path> {
102 self.test_paths.iter().filter(|p| {
103 println!("test path file name: {:?}", p.file_name());
104 p.is_dir()
105 }).map(PathBuf::as_ref)
106 }
107
108 pub fn is_extension_supported(&self, extension: &str) -> bool {
110 self.supported_file_extensions.iter().
111 find(|ext| &ext[..] == extension).is_some()
112 }
113
114 pub fn lookup_variable<'a>(&self,
116 name: &str,
117 variables: &'a mut HashMap<String, String>)
118 -> &'a str {
119 if !variables.contains_key(name) {
120 match self.variable_lookup.0(name) {
121 Some(initial_value) => {
122 variables.insert(name.to_owned(), initial_value.clone());
123 },
124 None => (),
125 }
126 }
127
128 variables.get(name).expect(&format!("no variable with the name '{}' exists", name))
129 }
130}
131
132impl Default for Config
133{
134 fn default() -> Self {
135 let mut extra_executable_search_paths = Vec::new();
136
137 if let Ok(current_exe) = std::env::current_exe() {
140 if let Some(parent) = current_exe.parent() {
141 extra_executable_search_paths.push(parent.to_owned());
142 }
143 }
144
145 Config {
146 supported_file_extensions: Vec::new(),
147 test_paths: Vec::new(),
148 constants: HashMap::new(),
149 variable_lookup: Config::DEFAULT_VARIABLE_LOOKUP,
150 cleanup_temporary_files: true,
151 save_artifacts_to_directory: None,
152 dump_variable_resolution: false,
153 always_show_stderr: false,
154 truncate_output_context_to_number_of_lines: Some(DEFAULT_MAX_OUTPUT_CONTEXT_LINE_COUNT),
155 extra_executable_search_paths,
156 shell: "bash".to_string(),
157 }
158 }
159}
160
161impl fmt::Debug for VariableLookup {
162 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
163 "<function>".fmt(fmt)
164 }
165}
166
167#[cfg(test)]
168mod test {
169 use super::*;
170
171 #[test]
172 fn lookup_variable_works_correctly() {
173 let config = Config {
174 variable_lookup: VariableLookup(|v| {
175 if v.contains("tempfile") { Some(format!("/tmp/temp-{}", v.as_bytes().as_ptr() as usize)) } else { None }
176 }),
177 constants: vec![("name".to_owned(), "bob".to_owned())].into_iter().collect(),
178 ..Config::default()
179 };
180 let mut variables = config.constants.clone();
181
182 assert_eq!("bob", config.lookup_variable("name", &mut variables),
184 "cannot lookup constants by name");
185 let first_temp = config.lookup_variable("first_tempfile", &mut variables).to_owned();
186 let second_temp = config.lookup_variable("second_tempfile", &mut variables).to_owned();
187
188 assert!(first_temp != second_temp,
189 "different temporary paths should be different");
190
191 assert_eq!(first_temp,
192 config.lookup_variable("first_tempfile", &mut variables),
193 "first temp has changed its value");
194
195 assert_eq!(second_temp,
196 config.lookup_variable("second_tempfile", &mut variables),
197 "second temp has changed its value");
198 }
199}
200