manifest/serde_ext.rs
1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Contains local mirrors and Serde annotations for FIDL types.
6//! See https://serde.rs/remote-derive.html.
7
8#![allow(missing_docs)]
9
10use fidl_fuchsia_fonts::{GenericFontFamily, Slant, Style2 as FidlStyle, Width};
11use serde::{Deserialize, Serialize};
12
13/// Generates Serde serialize and deserialize methods for types of `Option<T>`, where `T` is a type
14/// defined in a remote crate and is mirrored in a local type (https://serde.rs/remote-derive.html).
15///
16/// On its own, Serde can't handle enums that wrap remote types. This works around that limitation
17/// for the very specific case of `Option<T>`.
18///
19/// Expands to a mod that can be used by Serde's `"with"` attribute.
20///
21/// Example:
22/// ```
23/// mod remote_crate {
24/// pub enum Magic8BallResponse {
25/// Yes, Maybe, No, TryAgainLater
26/// }
27/// }
28/// ```
29/// ```
30/// mod local_crate {
31/// use remote_crate::Magic8BallResponse;
32/// use serde::{Deserialize, Serialize};
33///
34/// #[derive(Deserialize, Serialize)]
35/// #[serde(with = "Magic8BallResponse")]
36/// pub enum Magic8BallResponseDef {
37/// Yes, Maybe, No, TryAgainLater
38/// }
39///
40/// derive_opt!(
41/// OptMagic8BallResponse,
42/// Magic8BallResponse,
43/// Magic8BallResponseDef,
44/// "Magic8BallResponseDef");
45///
46/// #[derive(Deserialize, Serialize)]
47/// pub struct Responses {
48/// present: Magic8BallResponse,
49/// #[serde(with = "OptMagic8BallResponse")]
50/// hazy: Option<Magic8BallResponse>,
51/// }
52/// }
53///
54/// ```
55///
56/// Parameters:
57/// - `module`: Name of the generated module, e.g. `OptFidlTypeSerde`.
58/// - `remote_type`: Name of the remote type being mirrored, e.g. `SomeFidlType`.
59/// - `local_type`: Name of the local type that's mirroring the remote type, e.g. `SomeFidlTypeDef`.
60/// - `local_type_str`: The same as `local_type`, but wrapped in quotes.
61macro_rules! derive_opt {
62 ($module:ident, $remote_type:ty, $local_type:ty, $local_type_str:expr) => {
63 #[allow(non_snake_case, dead_code, missing_docs, explicit_outlives_requirements)]
64 pub mod $module {
65 use super::*;
66 use serde::{Deserialize, Deserializer, Serialize, Serializer};
67
68 #[doc = "Implementation of Serde's serialize"]
69 pub fn serialize<S>(
70 value: &Option<$remote_type>,
71 serializer: S,
72 ) -> Result<S::Ok, S::Error>
73 where
74 S: Serializer,
75 {
76 #[derive(Serialize)]
77 struct Wrapper<'a>(#[serde(with = $local_type_str)] &'a $remote_type);
78 value.as_ref().map(Wrapper).serialize(serializer)
79 }
80
81 #[doc = "Implementation of Serde's deserialize"]
82 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<$remote_type>, D::Error>
83 where
84 D: Deserializer<'de>,
85 {
86 #[derive(Deserialize)]
87 struct Wrapper(#[serde(with = $local_type_str)] $remote_type);
88
89 let helper = Option::deserialize(deserializer)?;
90 Ok(helper.map(|Wrapper(external)| external))
91 }
92 }
93 };
94}
95
96/// Local mirror of [`fidl_fuchsia_fonts::Style2`], for use in JSON serialization.
97///
98/// We can't just use a Serde remote type for `Style2` here because there are lots of other required
99/// traits that are not derived for FIDL tables.
100///
101/// `Ord` is derived for use with the manifest generator's `TryMergeGroups` trait. The ordering is
102/// by necessity arbitrary, comparing each field in sequence, recursively comparing enum values and
103/// numbers.
104#[allow(missing_docs)]
105#[derive(Clone, Debug, Default, Deserialize, Serialize, Eq, PartialEq, Hash, Ord, PartialOrd)]
106pub struct StyleOptions {
107 #[serde(default, with = "OptSlant", skip_serializing_if = "Option::is_none")]
108 pub slant: Option<Slant>,
109 #[serde(default, skip_serializing_if = "Option::is_none")]
110 pub weight: Option<u16>,
111 #[serde(default, with = "OptWidth", skip_serializing_if = "Option::is_none")]
112 pub width: Option<Width>,
113}
114
115impl From<FidlStyle> for StyleOptions {
116 fn from(fidl_style: FidlStyle) -> Self {
117 StyleOptions { slant: fidl_style.slant, weight: fidl_style.weight, width: fidl_style.width }
118 }
119}
120
121impl From<StyleOptions> for FidlStyle {
122 fn from(style_options: StyleOptions) -> Self {
123 FidlStyle {
124 slant: style_options.slant,
125 weight: style_options.weight,
126 width: style_options.width,
127 ..Default::default()
128 }
129 }
130}
131
132/// Local mirror of [`fidl_fuchsia_fonts::GenericFontFamily`], for use in JSON serialization.
133///
134/// Serialized values are in _kebab-case_, e.g. `"sans-serif"`.
135#[allow(missing_docs)]
136#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)]
137#[serde(remote = "GenericFontFamily", rename_all = "kebab-case")]
138pub enum GenericFontFamilyDef {
139 Serif,
140 SansSerif,
141 Monospace,
142 Cursive,
143 Fantasy,
144 SystemUi,
145 Emoji,
146 Math,
147 Fangsong,
148}
149
150derive_opt!(OptGenericFontFamily, GenericFontFamily, GenericFontFamilyDef, "GenericFontFamilyDef");
151
152/// Local mirror of [`fidl_fuchsia_fonts::Slant`], for use in JSON serialization.
153///
154/// Serialized values are _lowercase_, e.g. `"italic"`.
155#[allow(missing_docs)]
156#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)]
157#[serde(remote = "Slant", rename_all = "lowercase")]
158pub enum SlantDef {
159 Upright,
160 Italic,
161 Oblique,
162}
163
164derive_opt!(OptSlant, Slant, SlantDef, "SlantDef");
165
166/// Local mirror of [`fidl_fuchsia_fonts::Width`], for use in JSON serialization.
167///
168/// Serialized values are in _kebab-case_, e.g. `"semi-condensed"`.
169#[allow(missing_docs)]
170#[derive(Clone, Debug, Deserialize, Serialize, Eq, PartialEq, Hash)]
171#[serde(remote = "Width", rename_all = "kebab-case")]
172pub enum WidthDef {
173 UltraCondensed,
174 ExtraCondensed,
175 Condensed,
176 SemiCondensed,
177 Normal,
178 SemiExpanded,
179 Expanded,
180 ExtraExpanded,
181 UltraExpanded,
182}
183
184derive_opt!(OptWidth, Width, WidthDef, "WidthDef");