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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
// Take a look at the license at the top of the repository in the LICENSE file.

#[macro_export]
macro_rules! bitflags_serialize_impl {
    // this implementation serializes only flags using only one bit,
    // ignoring all other flags
    ($type:ty, single_bit_flags$(, $feature:expr)?) => {
        $(#[cfg(any(feature = $feature, docsrs))]
        #[cfg_attr(docsrs, doc(cfg(feature = $feature)))])?
        impl serde::Serialize for $type {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                let class = FlagsClass::with_type(Self::static_type()).unwrap();
                let this = self.to_value();

                let mut handled = Self::empty().to_value();
                let mut res = String::new();

                for v in class.values() {
                    let value = v.value();
                    if value.count_ones() == 1 && class.is_set(&this, value) && !class.is_set(&handled, value) {
                        if !res.is_empty() {
                            res.push('+');
                        }
                        res.push_str(v.nick());
                        handled = class.set(handled, value).expect("Failed to set flag");
                    }
                }

                serializer.serialize_str(&res)
            }
        }
    };

    // considers the flags using the most bits first
    ($type:ty, by_ones_decreasing$(, $feature:expr)?) => {
        $(#[cfg(any(feature = $feature, docsrs))]
        #[cfg_attr(docsrs, doc(cfg(feature = $feature)))])?
        impl serde::Serialize for $type {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                let mut handled = Self::empty();
                let mut res = String::new();

                static SORTED_VALUES: std::sync::OnceLock<Vec<(u32, String)>> = std::sync::OnceLock::new();

                let sorted_values = SORTED_VALUES.get_or_init(|| {
                    let class = FlagsClass::with_type(<$type>::static_type()).unwrap();
                    let mut sorted_values: Vec<(u32, String)> =
                        class.values().iter()
                            .map(|f| (f.value(), f.nick().to_owned()))
                            .collect();

                    sorted_values.sort_by(|(a, _), (b, _)| {
                        b.count_ones().cmp(&a.count_ones())
                    });

                    sorted_values
                });

                for (bits, nick) in sorted_values.iter() {
                    // not all values defined in the class are always also defined
                    // in the bitflags struct, see RTPBufferFlags for example
                    if let Some(value) = Self::from_bits(*bits) {
                        if !value.is_empty() && self.contains(value) && !handled.intersects(value) {
                            if !res.is_empty() {
                                res.push('+');
                            }
                            res.push_str(nick);
                            handled.insert(value);
                        }
                    }
                }

                serializer.serialize_str(&res)
            }
        }
    };
}

#[macro_export]
macro_rules! bitflags_deserialize_impl {
    ($type:ty$(, $feature:expr)?) => {
        $(#[cfg(any(feature = $feature, docsrs))]
        #[cfg_attr(docsrs, doc(cfg(feature = $feature)))])?
        impl<'de> serde::Deserialize<'de> for $type {
            fn deserialize<D: serde::de::Deserializer<'de>>(
                deserializer: D,
            ) -> std::result::Result<Self, D::Error> {
                skip_assert_initialized!();

                struct FlagsVisitor;

                impl<'de> serde::de::Visitor<'de> for FlagsVisitor {
                    type Value = $type;

                    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                        formatter.write_str(
                            "one or more mask names separated by plus signs, or the empty string",
                        )
                    }

                    fn visit_str<E: serde::de::Error>(
                        self,
                        value: &str,
                    ) -> std::result::Result<Self::Value, E> {
                        if value.is_empty() {
                            return Ok(Self::Value::empty());
                        }

                        let mut gvalue = unsafe { glib::Value::from_type_unchecked(Self::Value::static_type()) };
                        let tokens = value.split('+');
                        let class = FlagsClass::with_type(Self::Value::static_type()).unwrap();

                        for token in tokens {
                            gvalue = class.set_by_nick(gvalue, token).map_err(|_| {
                                serde::de::Error::custom(&format!("Invalid value: {}", token))
                            })?;
                        }

                        Ok(unsafe {
                            from_glib(glib::gobject_ffi::g_value_get_flags(
                                gvalue.to_glib_none().0,
                            ))
                        })
                    }
                }

                deserializer.deserialize_str(FlagsVisitor)
            }
        }
    };
}

#[macro_export]
macro_rules! bitflags_serde_impl {
    ($type:ty$(, $feature:expr)?) => {
        $crate::bitflags_serialize_impl!($type, single_bit_flags$(, $feature)?);
        $crate::bitflags_deserialize_impl!($type$(, $feature)?);
    };
}