buildkit_frontend/options/
default.rs1use std::collections::BTreeMap;
2use std::iter::once;
3
4use either::Either;
5use serde::Deserialize;
6
7#[derive(Debug, PartialEq, Deserialize)]
8#[serde(transparent)]
9pub struct Options {
10 inner: BTreeMap<String, OptionValue>,
11}
12
13#[derive(Debug, PartialEq, Deserialize)]
14#[serde(untagged)]
15enum OptionValue {
16 Flag(bool),
17 Single(String),
18 Multiple(Vec<String>),
19}
20
21impl Options {
22 pub fn has<S>(&self, name: S) -> bool
23 where
24 S: AsRef<str>,
25 {
26 match self.inner.get(name.as_ref()) {
27 Some(container) => match container {
28 OptionValue::Flag(exists) => *exists,
29 OptionValue::Single(_) => true,
30 OptionValue::Multiple(_) => true,
31 },
32
33 None => false,
34 }
35 }
36
37 pub fn is_flag_set<S>(&self, name: S) -> bool
38 where
39 S: AsRef<str>,
40 {
41 match self.inner.get(name.as_ref()) {
42 Some(container) => match container {
43 OptionValue::Flag(flag) => *flag,
44 OptionValue::Single(_) => false,
45 OptionValue::Multiple(_) => false,
46 },
47
48 None => false,
49 }
50 }
51
52 pub fn has_value<S1, S2>(&self, name: S1, value: S2) -> bool
53 where
54 S1: AsRef<str>,
55 S2: AsRef<str>,
56 {
57 match self.inner.get(name.as_ref()) {
58 Some(container) => match container {
59 OptionValue::Flag(_) => false,
60 OptionValue::Single(single) => single == value.as_ref(),
61 OptionValue::Multiple(values) => values.iter().any(|item| item == value.as_ref()),
62 },
63
64 None => false,
65 }
66 }
67
68 pub fn get<S>(&self, name: S) -> Option<&str>
69 where
70 S: AsRef<str>,
71 {
72 match self.inner.get(name.as_ref()) {
73 Some(container) => match container {
74 OptionValue::Flag(_) => None,
75 OptionValue::Single(value) => Some(value.as_str()),
76 OptionValue::Multiple(values) => values.iter().map(String::as_str).next(),
77 },
78
79 None => None,
80 }
81 }
82
83 pub fn iter<S>(&self, name: S) -> Option<impl Iterator<Item = &str>>
84 where
85 S: AsRef<str>,
86 {
87 match self.inner.get(name.as_ref()) {
88 Some(container) => match container {
89 OptionValue::Flag(_) => None,
90 OptionValue::Single(value) => Some(Either::Left(once(value.as_str()))),
91 OptionValue::Multiple(values) => {
92 Some(Either::Right(values.iter().map(String::as_str)))
93 }
94 },
95
96 None => None,
97 }
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::super::from_env;
104 use super::*;
105
106 #[test]
107 fn options_parsing() {
108 let options = from_env::<Options, _>(into_env(vec![
109 "name1",
110 "name2=true",
111 "name3=false",
112 "name4=",
113 "name5=value",
114 "name6=de=limiter",
115 "name7=false,true",
116 "name8=value1,value2,value3",
117 "name9=value1,val=ue2,value3",
118 "build-arg:name10",
119 "build-arg:name11=value",
120 ]))
121 .unwrap();
122
123 assert_eq!(options.inner["name1"], OptionValue::Flag(true));
124 assert_eq!(options.inner["name2"], OptionValue::Flag(true));
125 assert_eq!(options.inner["name3"], OptionValue::Flag(false));
126 assert_eq!(options.inner["name4"], OptionValue::Flag(true));
127
128 assert_eq!(options.inner["name5"], OptionValue::Single("value".into()));
129 assert_eq!(
130 options.inner["name6"],
131 OptionValue::Single("de=limiter".into())
132 );
133 assert_eq!(
134 options.inner["name7"],
135 OptionValue::Multiple(vec!["false".into(), "true".into()])
136 );
137 assert_eq!(
138 options.inner["name8"],
139 OptionValue::Multiple(vec!["value1".into(), "value2".into(), "value3".into()])
140 );
141 assert_eq!(
142 options.inner["name9"],
143 OptionValue::Multiple(vec!["value1".into(), "val=ue2".into(), "value3".into()])
144 );
145
146 assert_eq!(options.inner["name10"], OptionValue::Flag(true));
147 assert_eq!(options.inner["name11"], OptionValue::Single("value".into()));
148 }
149
150 #[test]
151 fn has_method() {
152 let options = from_env::<Options, _>(into_env(vec![
153 "option1",
154 "option2=true",
155 "option3=false",
156 "option4=true,false",
157 ]))
158 .unwrap();
159
160 assert_eq!(options.has("option1"), true);
161 assert_eq!(options.has("option2"), true);
162 assert_eq!(options.has("option3"), false);
163 assert_eq!(options.has("option4"), true);
164 }
165
166 #[test]
167 fn has_value_method() {
168 let options = from_env::<Options, _>(into_env(vec![
169 "option1",
170 "option2=true",
171 "option3=true,false,any_other",
172 ]))
173 .unwrap();
174
175 assert_eq!(options.has_value("option1", ""), false);
176 assert_eq!(options.has_value("option1", "any_other"), false);
177 assert_eq!(options.has_value("option2", ""), false);
178 assert_eq!(options.has_value("option2", "any_other"), false);
179 assert_eq!(options.has_value("option3", "true"), true);
180 assert_eq!(options.has_value("option3", "false"), true);
181 assert_eq!(options.has_value("option3", "any_other"), true);
182 assert_eq!(options.has_value("option3", "missing"), false);
183 }
184
185 #[test]
186 fn iter_method() {
187 let options = from_env::<Options, _>(into_env(vec![
188 "option1",
189 "option2=true",
190 "option3=true,false,any_other",
191 ]))
192 .unwrap();
193
194 assert!(options.iter("option1").is_none());
195 assert!(options.iter("option2").is_none());
196 assert!(options.iter("option4").is_none());
197
198 assert!(options.iter("option3").is_some());
199 assert_eq!(
200 options.iter("option3").unwrap().collect::<Vec<_>>(),
201 vec!["true", "false", "any_other"]
202 );
203 }
204
205 fn into_env(args: Vec<&'static str>) -> Vec<(String, String)> {
206 args.into_iter()
207 .enumerate()
208 .map(|(index, option)| {
209 (
210 format!("BUILDKIT_FRONTEND_OPT_{}", index),
211 String::from(option),
212 )
213 })
214 .collect()
215 }
216}