1use pulumi_gestalt_model::ToPulumiValue;
2
3pub type PulumiAny = pulumi_gestalt_model::PulumiValueMiddleware;
4
5#[macro_export]
10macro_rules! pulumi_any {
11 ($($tt:tt)+) => {
12 $crate::pulumi_any_v2_internal!($($tt)+)
13 };
14}
15
16#[doc(hidden)]
17#[macro_export]
18macro_rules! pulumi_any_v2_internal {
19 (null) => {
20 $crate::__private::pulumi_gestalt_model::__private::pulumi_value_middleware(
21 $crate::__private::pulumi_gestalt_model::PulumiValueContent::None,
22 )
23 };
24 ([$($tt:tt)*]) => {
25 $crate::pulumi_any_v2_internal_array!([] $($tt)* ,)
26 };
27 ({$($tt:tt)*}) => {
28 $crate::pulumi_any_v2_internal_object!([] $($tt)* ,)
29 };
30 ($other:expr) => {
31 $crate::__private::pulumi_gestalt_model::__private::to_pulumi_value_middleware($other)
32 };
33}
34
35#[doc(hidden)]
36#[macro_export]
37macro_rules! pulumi_any_v2_internal_array {
38 ([$($elems:expr,)*]) => {
39 $crate::__private::pulumi_gestalt_model::__private::pulumi_value_middleware_array(
40 vec![$($elems,)*]
41 )
42 };
43 ([$($elems:expr,)*] , $($rest:tt)*) => {
44 $crate::pulumi_any_v2_internal_array!([$($elems,)*] $($rest)*)
45 };
46 ([$($elems:expr,)*] null , $($rest:tt)*) => {
47 $crate::pulumi_any_v2_internal_array!(
48 [
49 $($elems,)*
50 $crate::pulumi_any_v2_internal!(null),
51 ]
52 $($rest)*
53 )
54 };
55 ([$($elems:expr,)*] [$($inner:tt)*] , $($rest:tt)*) => {
56 $crate::pulumi_any_v2_internal_array!(
57 [
58 $($elems,)*
59 $crate::pulumi_any_v2_internal!([$($inner)*]),
60 ]
61 $($rest)*
62 )
63 };
64 ([$($elems:expr,)*] {$($inner:tt)*} , $($rest:tt)*) => {
65 $crate::pulumi_any_v2_internal_array!(
66 [
67 $($elems,)*
68 $crate::pulumi_any_v2_internal!({$($inner)*}),
69 ]
70 $($rest)*
71 )
72 };
73 ([$($elems:expr,)*] $next:expr , $($rest:tt)*) => {
74 $crate::pulumi_any_v2_internal_array!(
75 [
76 $($elems,)*
77 $crate::pulumi_any_v2_internal!($next),
78 ]
79 $($rest)*
80 )
81 };
82}
83
84#[doc(hidden)]
85#[macro_export]
86macro_rules! pulumi_any_v2_internal_object {
87 ([$($fields:expr,)*]) => {
88 $crate::__private::pulumi_gestalt_model::__private::pulumi_value_middleware_object(
89 vec![$($fields,)*]
90 )
91 };
92 ([$($fields:expr,)*] , $($rest:tt)*) => {
93 $crate::pulumi_any_v2_internal_object!([$($fields,)*] $($rest)*)
94 };
95 ([$($fields:expr,)*] $key:tt : null , $($rest:tt)*) => {
96 $crate::pulumi_any_v2_internal_object!(
97 [
98 $($fields,)*
99 (
100 ::std::convert::Into::<::std::string::String>::into($key),
101 $crate::pulumi_any_v2_internal!(null),
102 ),
103 ]
104 $($rest)*
105 )
106 };
107 ([$($fields:expr,)*] $key:tt : [$($inner:tt)*] , $($rest:tt)*) => {
108 $crate::pulumi_any_v2_internal_object!(
109 [
110 $($fields,)*
111 (
112 ::std::convert::Into::<::std::string::String>::into($key),
113 $crate::pulumi_any_v2_internal!([$($inner)*]),
114 ),
115 ]
116 $($rest)*
117 )
118 };
119 ([$($fields:expr,)*] $key:tt : {$($inner:tt)*} , $($rest:tt)*) => {
120 $crate::pulumi_any_v2_internal_object!(
121 [
122 $($fields,)*
123 (
124 ::std::convert::Into::<::std::string::String>::into($key),
125 $crate::pulumi_any_v2_internal!({$($inner)*}),
126 ),
127 ]
128 $($rest)*
129 )
130 };
131 ([$($fields:expr,)*] $key:tt : $value:expr , $($rest:tt)*) => {
132 $crate::pulumi_any_v2_internal_object!(
133 [
134 $($fields,)*
135 (
136 ::std::convert::Into::<::std::string::String>::into($key),
137 $crate::pulumi_any_v2_internal!($value),
138 ),
139 ]
140 $($rest)*
141 )
142 };
143}
144
145pub trait ToPulumiAny {
146 fn to_pulumi_any(self) -> PulumiAny;
147}
148
149impl<T> ToPulumiAny for T
150where
151 T: ToPulumiValue + Send + Sync + 'static,
152{
153 fn to_pulumi_any(self) -> PulumiAny {
154 pulumi_gestalt_model::__private::to_pulumi_value_middleware(self)
155 }
156}
157
158#[cfg(test)]
159mod tests {
160 use super::ToPulumiAny;
161 use pulumi_gestalt_model::__private::futures::executor::block_on;
162 use pulumi_gestalt_model::{
163 Output as ModelOutput, PulumiValue, PulumiValueContent, ToPulumiValue,
164 };
165 use std::collections::HashSet;
166
167 struct CustomToPulumiValue {
168 id: i32,
169 }
170
171 impl ToPulumiValue for CustomToPulumiValue {
172 fn to_pulumi_value(&self) -> impl std::future::Future<Output = PulumiValue> + Send {
173 let id = self.id;
174 std::future::ready(PulumiValue {
175 content: PulumiValueContent::Object(vec![(
176 "custom-id".to_string(),
177 pv(PulumiValueContent::Integer(id + 1000)),
178 )]),
179 secret: false,
180 dependencies: HashSet::new(),
181 })
182 }
183 }
184
185 fn pv(content: PulumiValueContent) -> PulumiValue {
186 PulumiValue {
187 content,
188 secret: false,
189 dependencies: HashSet::new(),
190 }
191 }
192
193 fn pvs(content: PulumiValueContent) -> PulumiValue {
194 PulumiValue {
195 content,
196 secret: true,
197 dependencies: HashSet::new(),
198 }
199 }
200
201 #[test]
202 fn to_pulumi_any_converts_plain_value() {
203 let pulumi_any = "demo".to_pulumi_any();
204 let decoded = block_on(pulumi_any.to_pulumi_value());
205 assert_eq!(decoded, pv(PulumiValueContent::String("demo".to_string())));
206 }
207
208 #[test]
209 fn pulumi_any_macro_scalar() {
210 let integer = block_on(pulumi_any!(42).to_pulumi_value());
211 assert_eq!(integer, pv(PulumiValueContent::Integer(42)));
212
213 let float = block_on(pulumi_any!(1.5f64).to_pulumi_value());
214 assert_eq!(float, pv(PulumiValueContent::Number(1.5)));
215
216 let boolean = block_on(pulumi_any!(true).to_pulumi_value());
217 assert_eq!(boolean, pv(PulumiValueContent::Boolean(true)));
218
219 let string = block_on(pulumi_any!("hello").to_pulumi_value());
220 assert_eq!(string, pv(PulumiValueContent::String("hello".to_string())));
221 }
222
223 #[test]
224 fn pulumi_any_macro_array_and_object() {
225 let array = block_on(pulumi_any!([1, 2, 3]).to_pulumi_value());
226 assert_eq!(
227 array,
228 pv(PulumiValueContent::Array(vec![
229 pv(PulumiValueContent::Integer(1)),
230 pv(PulumiValueContent::Integer(2)),
231 pv(PulumiValueContent::Integer(3)),
232 ]))
233 );
234
235 let object = block_on(pulumi_any!({"name": "macro_test", "count": 5}).to_pulumi_value());
236 assert_eq!(
237 object,
238 pv(PulumiValueContent::Object(vec![
239 ("count".to_string(), pv(PulumiValueContent::Integer(5))),
240 (
241 "name".to_string(),
242 pv(PulumiValueContent::String("macro_test".to_string()))
243 ),
244 ]))
245 );
246 }
247
248 #[test]
249 fn pulumi_any_macro_nested_and_trailing_commas() {
250 let value = block_on(
251 pulumi_any!({
252 "items": [
253 1,
254 {"name": "a",},
255 ],
256 "ok": true,
257 })
258 .to_pulumi_value(),
259 );
260
261 assert_eq!(
262 value,
263 pv(PulumiValueContent::Object(vec![
264 (
265 "items".to_string(),
266 pv(PulumiValueContent::Array(vec![
267 pv(PulumiValueContent::Integer(1)),
268 pv(PulumiValueContent::Object(vec![(
269 "name".to_string(),
270 pv(PulumiValueContent::String("a".to_string())),
271 )])),
272 ])),
273 ),
274 ("ok".to_string(), pv(PulumiValueContent::Boolean(true))),
275 ]))
276 );
277 }
278
279 #[test]
280 fn pulumi_any_macro_nested_model_outputs() {
281 let value = block_on(
282 pulumi_any!({
283 "known": ModelOutput::new(7i32),
284 "secret": ModelOutput::new_secret("sensitive"),
285 "unknown": ModelOutput::<i32>::new_nothing(),
286 "nested": [ModelOutput::new(true)],
287 })
288 .to_pulumi_value(),
289 );
290
291 assert_eq!(
292 value,
293 pvs(PulumiValueContent::Object(vec![
294 ("known".to_string(), pv(PulumiValueContent::Integer(7))),
295 (
296 "nested".to_string(),
297 pv(PulumiValueContent::Array(vec![pv(
298 PulumiValueContent::Boolean(true)
299 )])),
300 ),
301 (
302 "secret".to_string(),
303 pvs(PulumiValueContent::String("sensitive".to_string())),
304 ),
305 ("unknown".to_string(), pv(PulumiValueContent::Nothing)),
306 ]))
307 );
308 }
309
310 #[test]
311 fn pulumi_any_macro_nested_object_with_parenthesized_expr_values() {
312 let a_string = "name".to_string();
313 let a_list = vec!["x".to_string(), "y".to_string(), "z".to_string()];
314
315 let value = block_on(
316 pulumi_any!({
317 "anObject": {
318 "items": ((a_list).clone()),
319 "name": ((a_string).clone()),
320 },
321 })
322 .to_pulumi_value(),
323 );
324
325 let value_without_trailing_commas = block_on(
326 pulumi_any!({
327 "anObject": {
328 "items": ((a_list).clone()),
329 "name": ((a_string).clone())
330 }
331 })
332 .to_pulumi_value(),
333 );
334
335 assert_eq!(
336 value,
337 pv(PulumiValueContent::Object(vec![(
338 "anObject".to_string(),
339 pv(PulumiValueContent::Object(vec![
340 (
341 "items".to_string(),
342 pv(PulumiValueContent::Array(vec![
343 pv(PulumiValueContent::String("x".to_string())),
344 pv(PulumiValueContent::String("y".to_string())),
345 pv(PulumiValueContent::String("z".to_string())),
346 ])),
347 ),
348 (
349 "name".to_string(),
350 pv(PulumiValueContent::String("name".to_string())),
351 ),
352 ])),
353 )]))
354 );
355 assert_eq!(
356 value, value_without_trailing_commas,
357 "object literals should parse with or without trailing commas"
358 );
359 }
360
361 #[test]
362 fn pulumi_any_macro_array_without_trailing_comma() {
363 let value_with_trailing_comma = block_on(pulumi_any!([1, 2, 3,]).to_pulumi_value());
364 let value_without_trailing_comma = block_on(pulumi_any!([1, 2, 3]).to_pulumi_value());
365
366 assert_eq!(
367 value_with_trailing_comma,
368 pv(PulumiValueContent::Array(vec![
369 pv(PulumiValueContent::Integer(1)),
370 pv(PulumiValueContent::Integer(2)),
371 pv(PulumiValueContent::Integer(3)),
372 ]))
373 );
374 assert_eq!(
375 value_with_trailing_comma, value_without_trailing_comma,
376 "array literals should parse with or without trailing commas"
377 );
378 }
379
380 #[test]
381 fn pulumi_any_macro_object_sorts_keys() {
382 let value = block_on(
383 pulumi_any!({
384 "z": 1,
385 "a": 2,
386 })
387 .to_pulumi_value(),
388 );
389
390 assert_eq!(
391 value,
392 pv(PulumiValueContent::Object(vec![
393 ("a".to_string(), pv(PulumiValueContent::Integer(2))),
394 ("z".to_string(), pv(PulumiValueContent::Integer(1))),
395 ]))
396 );
397 }
398
399 #[test]
400 fn pulumi_any_macro_object_last_wins_for_duplicate_keys() {
401 let value = block_on(
402 pulumi_any!({
403 "a": 1,
404 "a": 2,
405 "b": 3,
406 })
407 .to_pulumi_value(),
408 );
409
410 assert_eq!(
411 value,
412 pv(PulumiValueContent::Object(vec![
413 ("a".to_string(), pv(PulumiValueContent::Integer(2))),
414 ("b".to_string(), pv(PulumiValueContent::Integer(3))),
415 ]))
416 );
417 }
418
419 #[test]
420 fn pulumi_any_macro_array_propagates_secret_from_nested_output() {
421 let value = block_on(
422 pulumi_any!([ModelOutput::new(1i32), ModelOutput::new_secret(2i32),]).to_pulumi_value(),
423 );
424
425 assert_eq!(
426 value,
427 pvs(PulumiValueContent::Array(vec![
428 pv(PulumiValueContent::Integer(1)),
429 pvs(PulumiValueContent::Integer(2)),
430 ]))
431 );
432 }
433
434 #[test]
435 fn pulumi_any_macro_uses_custom_to_pulumi_value_impl() {
436 let value = block_on(
437 pulumi_any!({
438 "custom": CustomToPulumiValue { id: 7 },
439 })
440 .to_pulumi_value(),
441 );
442
443 assert_eq!(
444 value,
445 pv(PulumiValueContent::Object(vec![(
446 "custom".to_string(),
447 pv(PulumiValueContent::Object(vec![(
448 "custom-id".to_string(),
449 pv(PulumiValueContent::Integer(1007)),
450 )])),
451 )]))
452 );
453 }
454}