pub_just/
settings.rs

1use super::*;
2
3pub const DEFAULT_SHELL: &str = "sh";
4pub const DEFAULT_SHELL_ARGS: &[&str] = &["-cu"];
5pub const WINDOWS_POWERSHELL_SHELL: &str = "powershell.exe";
6pub const WINDOWS_POWERSHELL_ARGS: &[&str] = &["-NoLogo", "-Command"];
7
8#[derive(Debug, PartialEq, Serialize, Default)]
9pub struct Settings<'src> {
10  pub allow_duplicate_recipes: bool,
11  pub allow_duplicate_variables: bool,
12  pub dotenv_filename: Option<String>,
13  pub dotenv_load: bool,
14  pub dotenv_path: Option<PathBuf>,
15  pub dotenv_required: bool,
16  pub export: bool,
17  pub fallback: bool,
18  pub ignore_comments: bool,
19  pub positional_arguments: bool,
20  pub quiet: bool,
21  #[serde(skip)]
22  pub script_interpreter: Option<Interpreter<'src>>,
23  pub shell: Option<Interpreter<'src>>,
24  pub tempdir: Option<String>,
25  pub unstable: bool,
26  pub windows_powershell: bool,
27  pub windows_shell: Option<Interpreter<'src>>,
28  pub working_directory: Option<PathBuf>,
29}
30
31impl<'src> Settings<'src> {
32  pub fn from_table(sets: Table<'src, Set<'src>>) -> Self {
33    let mut settings = Self::default();
34
35    for (_name, set) in sets {
36      match set.value {
37        Setting::AllowDuplicateRecipes(allow_duplicate_recipes) => {
38          settings.allow_duplicate_recipes = allow_duplicate_recipes;
39        }
40        Setting::AllowDuplicateVariables(allow_duplicate_variables) => {
41          settings.allow_duplicate_variables = allow_duplicate_variables;
42        }
43        Setting::DotenvFilename(filename) => {
44          settings.dotenv_filename = Some(filename.cooked);
45        }
46        Setting::DotenvLoad(dotenv_load) => {
47          settings.dotenv_load = dotenv_load;
48        }
49        Setting::DotenvPath(path) => {
50          settings.dotenv_path = Some(PathBuf::from(path.cooked));
51        }
52        Setting::DotenvRequired(dotenv_required) => {
53          settings.dotenv_required = dotenv_required;
54        }
55        Setting::Export(export) => {
56          settings.export = export;
57        }
58        Setting::Fallback(fallback) => {
59          settings.fallback = fallback;
60        }
61        Setting::IgnoreComments(ignore_comments) => {
62          settings.ignore_comments = ignore_comments;
63        }
64        Setting::PositionalArguments(positional_arguments) => {
65          settings.positional_arguments = positional_arguments;
66        }
67        Setting::Quiet(quiet) => {
68          settings.quiet = quiet;
69        }
70        Setting::ScriptInterpreter(script_interpreter) => {
71          settings.script_interpreter = Some(script_interpreter);
72        }
73        Setting::Shell(shell) => {
74          settings.shell = Some(shell);
75        }
76        Setting::Unstable(unstable) => {
77          settings.unstable = unstable;
78        }
79        Setting::WindowsPowerShell(windows_powershell) => {
80          settings.windows_powershell = windows_powershell;
81        }
82        Setting::WindowsShell(windows_shell) => {
83          settings.windows_shell = Some(windows_shell);
84        }
85        Setting::Tempdir(tempdir) => {
86          settings.tempdir = Some(tempdir.cooked);
87        }
88        Setting::WorkingDirectory(working_directory) => {
89          settings.working_directory = Some(working_directory.cooked.into());
90        }
91      }
92    }
93
94    settings
95  }
96
97  pub fn shell_command(&self, config: &Config) -> Command {
98    let (command, args) = self.shell(config);
99
100    let mut cmd = Command::new(command);
101
102    cmd.args(args);
103
104    cmd
105  }
106
107  pub fn shell<'a>(&'a self, config: &'a Config) -> (&'a str, Vec<&'a str>) {
108    match (&config.shell, &config.shell_args) {
109      (Some(shell), Some(shell_args)) => (shell, shell_args.iter().map(String::as_ref).collect()),
110      (Some(shell), None) => (shell, DEFAULT_SHELL_ARGS.to_vec()),
111      (None, Some(shell_args)) => (
112        DEFAULT_SHELL,
113        shell_args.iter().map(String::as_ref).collect(),
114      ),
115      (None, None) => {
116        if let (true, Some(shell)) = (cfg!(windows), &self.windows_shell) {
117          (
118            shell.command.cooked.as_ref(),
119            shell
120              .arguments
121              .iter()
122              .map(|argument| argument.cooked.as_ref())
123              .collect(),
124          )
125        } else if cfg!(windows) && self.windows_powershell {
126          (WINDOWS_POWERSHELL_SHELL, WINDOWS_POWERSHELL_ARGS.to_vec())
127        } else if let Some(shell) = &self.shell {
128          (
129            shell.command.cooked.as_ref(),
130            shell
131              .arguments
132              .iter()
133              .map(|argument| argument.cooked.as_ref())
134              .collect(),
135          )
136        } else {
137          (DEFAULT_SHELL, DEFAULT_SHELL_ARGS.to_vec())
138        }
139      }
140    }
141  }
142}
143
144#[cfg(test)]
145mod tests {
146  use super::*;
147
148  #[test]
149  fn default_shell() {
150    let settings = Settings::default();
151
152    let config = Config {
153      shell_command: false,
154      ..testing::config(&[])
155    };
156
157    assert_eq!(settings.shell(&config), ("sh", vec!["-cu"]));
158  }
159
160  #[test]
161  fn default_shell_powershell() {
162    let settings = Settings {
163      windows_powershell: true,
164      ..Default::default()
165    };
166
167    let config = Config {
168      shell_command: false,
169      ..testing::config(&[])
170    };
171
172    if cfg!(windows) {
173      assert_eq!(
174        settings.shell(&config),
175        ("powershell.exe", vec!["-NoLogo", "-Command"])
176      );
177    } else {
178      assert_eq!(settings.shell(&config), ("sh", vec!["-cu"]));
179    }
180  }
181
182  #[test]
183  fn overwrite_shell() {
184    let settings = Settings::default();
185
186    let config = Config {
187      shell_command: true,
188      shell: Some("lol".to_string()),
189      shell_args: Some(vec!["-nice".to_string()]),
190      ..testing::config(&[])
191    };
192
193    assert_eq!(settings.shell(&config), ("lol", vec!["-nice"]));
194  }
195
196  #[test]
197  fn overwrite_shell_powershell() {
198    let settings = Settings {
199      windows_powershell: true,
200      ..Default::default()
201    };
202
203    let config = Config {
204      shell_command: true,
205      shell: Some("lol".to_string()),
206      shell_args: Some(vec!["-nice".to_string()]),
207      ..testing::config(&[])
208    };
209
210    assert_eq!(settings.shell(&config), ("lol", vec!["-nice"]));
211  }
212
213  #[test]
214  fn shell_cooked() {
215    let settings = Settings {
216      shell: Some(Interpreter {
217        command: StringLiteral {
218          kind: StringKind::from_token_start("\"").unwrap(),
219          raw: "asdf.exe",
220          cooked: "asdf.exe".to_string(),
221          expand: false,
222        },
223        arguments: vec![StringLiteral {
224          kind: StringKind::from_token_start("\"").unwrap(),
225          raw: "-nope",
226          cooked: "-nope".to_string(),
227          expand: false,
228        }],
229      }),
230      ..Default::default()
231    };
232
233    let config = Config {
234      shell_command: false,
235      ..testing::config(&[])
236    };
237
238    assert_eq!(settings.shell(&config), ("asdf.exe", vec!["-nope"]));
239  }
240
241  #[test]
242  fn shell_present_but_not_shell_args() {
243    let settings = Settings {
244      windows_powershell: true,
245      ..Default::default()
246    };
247
248    let config = Config {
249      shell: Some("lol".to_string()),
250      ..testing::config(&[])
251    };
252
253    assert_eq!(settings.shell(&config).0, "lol");
254  }
255
256  #[test]
257  fn shell_args_present_but_not_shell() {
258    let settings = Settings {
259      windows_powershell: true,
260      ..Default::default()
261    };
262
263    let config = Config {
264      shell_command: false,
265      shell_args: Some(vec!["-nice".to_string()]),
266      ..testing::config(&[])
267    };
268
269    assert_eq!(settings.shell(&config), ("sh", vec!["-nice"]));
270  }
271}