Skip to main content

pulumi_gestalt_rust/
pulumi_any.rs

1use pulumi_gestalt_model::ToPulumiValue;
2
3pub type PulumiAny = pulumi_gestalt_model::PulumiValueMiddleware;
4
5/// Construct a [`PulumiAny`] value from JSON-like literals.
6///
7/// The syntax supports nested arrays/objects, trailing commas,
8/// and values implementing [`pulumi_gestalt_model::ToPulumiValue`].
9#[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}