buildkit_frontend/options/
deserializer.rs1use std::io::Cursor;
2use std::iter::empty;
3
4use failure::Error;
5use serde::de::value::{MapDeserializer, SeqDeserializer};
6use serde::de::{self, DeserializeOwned, IntoDeserializer, Visitor};
7use serde::forward_to_deserialize_any;
8
9pub fn from_env<T, I>(pairs: I) -> Result<T, Error>
10where
11 T: DeserializeOwned,
12 I: IntoIterator<Item = (String, String)>,
13{
14 let owned_pairs = pairs.into_iter().collect::<Vec<_>>();
15 let pairs = {
16 owned_pairs.iter().filter_map(|(name, value)| {
17 if name.starts_with("BUILDKIT_FRONTEND_OPT_") {
18 Some(value)
19 } else {
20 None
21 }
22 })
23 };
24
25 let deserializer = EnvDeserializer {
26 vals: pairs.map(|value| extract_name_and_value(&value)),
27 };
28
29 T::deserialize(deserializer).map_err(Error::from)
30}
31
32#[derive(Debug)]
33struct EnvDeserializer<P> {
34 vals: P,
35}
36
37#[derive(Debug)]
38enum EnvValue<'de> {
39 Flag,
40 Json(&'de str),
41 Text(&'de str),
42}
43
44#[derive(Debug)]
45struct EnvItem<'de>(&'de str);
46
47fn extract_name_and_value(mut raw_value: &str) -> (&str, EnvValue) {
48 if raw_value.starts_with("build-arg:") {
49 raw_value = raw_value.trim_start_matches("build-arg:");
50 }
51
52 let mut parts = raw_value.splitn(2, '=');
53 let name = parts.next().unwrap();
54
55 match parts.next() {
56 None => (name, EnvValue::Flag),
57 Some(text) if text.is_empty() => (name, EnvValue::Flag),
58 Some(text) if &text[0..1] == "[" || &text[0..1] == "{" => (name, EnvValue::Json(text)),
59 Some(text) => (name, EnvValue::Text(text)),
60 }
61}
62
63impl<'de> IntoDeserializer<'de, serde::de::value::Error> for EnvValue<'de> {
64 type Deserializer = Self;
65
66 fn into_deserializer(self) -> Self::Deserializer {
67 self
68 }
69}
70
71impl<'de> IntoDeserializer<'de, serde::de::value::Error> for EnvItem<'de> {
72 type Deserializer = Self;
73
74 fn into_deserializer(self) -> Self::Deserializer {
75 self
76 }
77}
78
79impl<'de> EnvItem<'de> {
80 fn infer<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, serde::de::value::Error> {
81 match self.0 {
82 "true" => visitor.visit_bool(true),
83 "false" => visitor.visit_bool(false),
84
85 _ => visitor.visit_str(self.0),
86 }
87 }
88
89 fn json<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, serde::de::value::Error> {
90 use serde::de::Deserializer;
91 use serde::de::Error;
92
93 serde_json::Deserializer::from_reader(Cursor::new(self.0))
94 .deserialize_any(visitor)
95 .map_err(serde::de::value::Error::custom)
96 }
97}
98
99impl<'de, P> de::Deserializer<'de> for EnvDeserializer<P>
100where
101 P: Iterator<Item = (&'de str, EnvValue<'de>)>,
102{
103 type Error = serde::de::value::Error;
104
105 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
106 visitor.visit_map(MapDeserializer::new(self.vals))
107 }
108
109 forward_to_deserialize_any! {
110 bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
111 bytes byte_buf option unit unit_struct newtype_struct seq tuple
112 tuple_struct map struct enum identifier ignored_any
113 }
114}
115
116macro_rules! forward_parsed_values_env_value {
118 ($($ty:ident => $method:ident,)*) => {
119 $(
120 fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
121 where V: de::Visitor<'de>
122 {
123 match self {
124 EnvValue::Flag => self.deserialize_any(visitor),
125 EnvValue::Json(_) => self.deserialize_any(visitor),
126 EnvValue::Text(contents) => {
127 match contents.parse::<$ty>() {
128 Ok(val) => val.into_deserializer().$method(visitor),
129 Err(e) => Err(de::Error::custom(format_args!("{} while parsing value '{}'", e, contents)))
130 }
131 }
132 }
133 }
134 )*
135 }
136}
137
138macro_rules! forward_parsed_values_env_item {
139 ($($ty:ident => $method:ident,)*) => {
140 $(
141 fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
142 where V: de::Visitor<'de>
143 {
144 match self.0.parse::<$ty>() {
145 Ok(val) => val.into_deserializer().$method(visitor),
146 Err(e) => Err(de::Error::custom(format_args!("{} while parsing value '{}'", e, self.0)))
147 }
148 }
149 )*
150 }
151}
152
153impl<'de> de::Deserializer<'de> for EnvValue<'de> {
154 type Error = serde::de::value::Error;
155
156 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
157 match self {
158 EnvValue::Flag => visitor.visit_bool(true),
159 EnvValue::Json(contents) => EnvItem(contents).json(visitor),
160 EnvValue::Text(contents) => {
161 if !contents.contains(',') {
162 EnvItem(contents).infer(visitor)
163 } else {
164 SeqDeserializer::new(contents.split(',')).deserialize_seq(visitor)
165 }
166 }
167 }
168 }
169
170 fn deserialize_seq<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
171 match self {
172 EnvValue::Flag => SeqDeserializer::new(empty::<&'de str>()).deserialize_seq(visitor),
173 EnvValue::Json(contents) => EnvItem(contents).json(visitor),
174 EnvValue::Text(contents) => {
175 SeqDeserializer::new(contents.split(',')).deserialize_seq(visitor)
176 }
177 }
178 }
179
180 fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
181 visitor.visit_some(self)
182 }
183
184 forward_parsed_values_env_value! {
185 bool => deserialize_bool,
186 u8 => deserialize_u8,
187 u16 => deserialize_u16,
188 u32 => deserialize_u32,
189 u64 => deserialize_u64,
190 u128 => deserialize_u128,
191 i8 => deserialize_i8,
192 i16 => deserialize_i16,
193 i32 => deserialize_i32,
194 i64 => deserialize_i64,
195 i128 => deserialize_i128,
196 f32 => deserialize_f32,
197 f64 => deserialize_f64,
198 }
199
200 forward_to_deserialize_any! {
201 byte_buf
202 bytes
203 char
204 enum
205 identifier
206 ignored_any
207 map
208 newtype_struct
209 str
210 string
211 struct
212 tuple
213 tuple_struct
214 unit
215 unit_struct
216 }
217}
218
219impl<'de> de::Deserializer<'de> for EnvItem<'de> {
220 type Error = serde::de::value::Error;
221
222 fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
223 self.0.into_deserializer().deserialize_any(visitor)
224 }
225
226 fn deserialize_map<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Self::Error> {
227 self.json(visitor)
228 }
229
230 fn deserialize_struct<V: Visitor<'de>>(
231 self,
232 _: &'static str,
233 _: &'static [&'static str],
234 visitor: V,
235 ) -> Result<V::Value, Self::Error> {
236 self.json(visitor)
237 }
238
239 forward_parsed_values_env_item! {
240 bool => deserialize_bool,
241 u8 => deserialize_u8,
242 u16 => deserialize_u16,
243 u32 => deserialize_u32,
244 u64 => deserialize_u64,
245 u128 => deserialize_u128,
246 i8 => deserialize_i8,
247 i16 => deserialize_i16,
248 i32 => deserialize_i32,
249 i64 => deserialize_i64,
250 i128 => deserialize_i128,
251 f32 => deserialize_f32,
252 f64 => deserialize_f64,
253 }
254
255 forward_to_deserialize_any! {
256 byte_buf
257 bytes
258 char
259 enum
260 identifier
261 ignored_any
262 newtype_struct
263 option
264 seq
265 str
266 string
267 tuple
268 tuple_struct
269 unit
270 unit_struct
271 }
272}