blob: 0edf068a29ce221ee24310b063f356716a33f09f [file] [log] [blame]
UebelAndrefa943c72024-10-01 11:42:22 -07001//! Utilities for parsing [Args](https://bazel.build/rules/lib/builtins/Args.html) param files.
2
3use std::path::Path;
4
5/// The format for an [Args param file[(https://bazel.build/rules/lib/builtins/Args.html#set_param_file_format).
6#[derive(Debug)]
7pub enum ActionArgsFormat {
8 /// Each item (argument name or value) is written verbatim to the param
9 /// file with a newline character following it.
10 Multiline,
11
12 /// Same as [Self::Multiline], but the items are shell-quoted.
13 Shell,
14
15 /// Same as [Self::Multiline], but (1) only flags (beginning with '--')
16 /// are written to the param file, and (2) the values of the flags, if
17 /// any, are written on the same line with a '=' separator. This is the
18 /// format expected by the Abseil flags library.
19 FlagPerLine,
20}
21
22impl Default for ActionArgsFormat {
23 fn default() -> Self {
24 Self::Shell
25 }
26}
27
28/// Parsed [`ctx.action.args`](https://bazel.build/rules/lib/builtins/Args.html) params.
29type ActionArgv = Vec<String>;
30
31/// Parse an [Args](https://bazel.build/rules/lib/builtins/Args.html) param file string into an argv list.
32pub fn parse_args_with_fmt(text: String, fmt: ActionArgsFormat) -> ActionArgv {
33 text.lines()
34 .map(|s| match fmt {
35 ActionArgsFormat::Shell => {
36 if s.starts_with('\'') && s.ends_with('\'') {
37 s[1..s.len() - 1].to_owned()
38 } else {
39 s.to_owned()
40 }
41 }
42 _ => s.to_owned(),
43 })
44 .collect()
45}
46
47/// Parse an [Args](https://bazel.build/rules/lib/builtins/Args.html) param file string into an argv list.
48pub fn parse_args(text: String) -> ActionArgv {
49 parse_args_with_fmt(text, ActionArgsFormat::default())
50}
51
52/// Parse an [Args](https://bazel.build/rules/lib/builtins/Args.html) param file into an argv list.
53pub fn try_parse_args_with_fmt(
54 path: &Path,
55 fmt: ActionArgsFormat,
56) -> Result<ActionArgv, std::io::Error> {
57 let text = std::fs::read_to_string(path)?;
58 Ok(parse_args_with_fmt(text, fmt))
59}
60
61/// Parse an [Args](https://bazel.build/rules/lib/builtins/Args.html) param file into an argv list.
62pub fn try_parse_args(path: &Path) -> Result<ActionArgv, std::io::Error> {
63 let text = std::fs::read_to_string(path)?;
64 Ok(parse_args(text))
65}
66
67#[cfg(test)]
68mod test {
69 use std::path::PathBuf;
70
71 use super::*;
72
73 const TEST_ARGS: [&str; 5] = ["foo", "-bar", "'baz'", "'--qux=quux'", "--quuz='corge'"];
74
75 #[test]
76 fn test_multiline_string() {
77 let text = TEST_ARGS.join("\n");
78
79 let args = parse_args_with_fmt(text, ActionArgsFormat::Multiline);
80 assert_eq!(
81 vec!["foo", "-bar", "'baz'", "'--qux=quux'", "--quuz='corge'"],
82 args
83 )
84 }
85
86 #[test]
87 fn test_shell_string() {
88 let text = TEST_ARGS.join("\n");
89
90 let args = parse_args_with_fmt(text, ActionArgsFormat::Shell);
91 assert_eq!(
92 vec!["foo", "-bar", "baz", "--qux=quux", "--quuz='corge'"],
93 args
94 )
95 }
96
97 #[test]
98 fn test_flag_per_line_string() {
99 let text = TEST_ARGS.join("\n");
100
101 let args = parse_args_with_fmt(text, ActionArgsFormat::FlagPerLine);
102 assert_eq!(
103 vec!["foo", "-bar", "'baz'", "'--qux=quux'", "--quuz='corge'"],
104 args
105 )
106 }
107
108 #[test]
109 fn test_from_file() {
110 let text = TEST_ARGS.join("\n");
111
112 let test_tempdir = PathBuf::from(std::env::var("TEST_TMPDIR").unwrap());
113 let test_file = test_tempdir.join("test_from_file.txt");
114
115 assert!(try_parse_args(&test_file).is_err());
116
117 std::fs::write(&test_file, text).unwrap();
118
119 let args = try_parse_args(&test_file).unwrap();
120 assert_eq!(
121 vec!["foo", "-bar", "baz", "--qux=quux", "--quuz='corge'"],
122 args
123 )
124 }
125}