1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#[macro_export]
/// Shorthand for creating a FIDL table in a way that will not cause build breakages if new fields
/// are added to the table in the future.
///
/// - The values do not have to be wrapped in `Some`; this is inferred automatically.
/// - Empty fields can either be omitted or explicitly set to `None`.
/// - When a field name matches the name of an in-scope variable or parameter, a shorthand notation
///   is available (just like in Rust struct initializers).
///
/// Example:
/// ```rust
/// use fidl_fuchsia_intl::{CalendarId, LocaleId, Profile, TemperatureUnit};
///
/// let calendars = vec![CalendarId { id: "gregorian".to_string() }];
/// let time_zones = None;
///
/// let table = fable! {
///   Profile {
///     locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
///     // `Some` can be omitted
///     temperature_unit: TemperatureUnit::Fahrenheit,
///     // Shorthand notation when the field and variable names match
///     calendars,
///     time_zones,
///   }
/// };
/// ```
macro_rules! fable {
    // Entry point
    ($fidl_type:path { $($rest:tt)* }) => {
        {
            use $fidl_type as FidlType;
            let mut _table = FidlType::default();
            fable!(@internal _table $($rest)*);
            _table
        }
    };

    // Full notation
    (@internal $table:ident $field:ident : $value:expr, $($rest:tt)*) => {
        $table.$field = ($value).into();
        fable!(@internal $table $($rest)*);
    };

    // Shorthand notation
    (@internal $table:ident $field:ident, $($rest:tt)*) => {
        $table.$field = $field.into();
        fable!(@internal $table $($rest)*);
    };

    // End
    (@internal $table:ident) => {};
}

#[cfg(test)]
mod tests {
    use {
        super::*,
        fidl_fuchsia_intl::{CalendarId, LocaleId, Profile, TemperatureUnit},
    };

    #[test]
    fn test_combinations() {
        let mut expected = Profile::default();
        expected.locales = Some(vec![LocaleId { id: "en-US".to_string() }]);
        expected.temperature_unit = Some(TemperatureUnit::Fahrenheit);
        expected.calendars = Some(vec![CalendarId { id: "gregory".to_string() }]);

        let calendars = vec![CalendarId { id: "gregory".to_string() }];
        let time_zones = None;

        let actual = fable! {
            Profile {
                locales: Some(vec![LocaleId { id: "en-US".to_string() }]),
                temperature_unit: TemperatureUnit::Fahrenheit,
                calendars,
                time_zones,
            }
        };

        assert_eq!(actual, expected);
    }
}