reproto_core/
rp_enum.rs

1//! Model for enums
2
3use errors::Result;
4use serde::Serialize;
5use std::fmt;
6use std::vec;
7use {BigInt, Diagnostics, Flavor, Loc, RpCode, RpNumber, RpReg, RpValue, Span, Translate,
8     Translator};
9
10decl_body!(pub struct RpEnumBody<F> {
11    /// The type of the variant.
12    pub enum_type: F::EnumType,
13    /// Variants in the enum.
14    pub variants: RpVariants<F>,
15    /// Custom code blocks in the enum.
16    pub codes: Vec<Loc<RpCode>>,
17});
18
19impl<F: 'static, T> Translate<T> for RpEnumBody<F>
20where
21    F: Flavor,
22    T: Translator<Source = F>,
23{
24    type Source = F;
25    type Out = RpEnumBody<T::Target>;
26
27    /// Translate into different flavor.
28    fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpEnumBody<T::Target>> {
29        translator.visit(diag, &self.name)?;
30
31        let name = translator.translate_local_name(diag, RpReg::Enum, self.name)?;
32        let enum_type = translator.translate_enum_type(diag, self.enum_type)?;
33
34        Ok(RpEnumBody {
35            name: name,
36            ident: self.ident,
37            comment: self.comment,
38            decls: self.decls.translate(diag, translator)?,
39            enum_type,
40            variants: self.variants.translate(diag, translator)?,
41            codes: self.codes,
42        })
43    }
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum RpVariantValue<'a> {
48    String(&'a str),
49    Number(&'a RpNumber),
50}
51
52impl<'a> From<&'a RpNumber> for RpVariantValue<'a> {
53    fn from(value: &'a RpNumber) -> Self {
54        RpVariantValue::Number(value)
55    }
56}
57
58impl<'a> From<&'a String> for RpVariantValue<'a> {
59    fn from(value: &'a String) -> Self {
60        RpVariantValue::String(value.as_str())
61    }
62}
63
64impl<'a> fmt::Display for RpVariantValue<'a> {
65    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
66        use self::RpVariantValue::*;
67
68        match *self {
69            String(string) => write!(fmt, "{:?}", string),
70            Number(value) => value.fmt(fmt),
71        }
72    }
73}
74
75/// A cheap, type-erasured variant that can be used for value comparisons.
76///
77/// This is typically created using `RpVariants::iter()`.
78#[derive(Debug, Clone, Copy)]
79pub struct RpVariantRef<'a, F: 'static>
80where
81    F: Flavor,
82{
83    pub span: Span,
84    pub name: &'a F::Name,
85    pub ident: &'a Loc<String>,
86    pub comment: &'a Vec<String>,
87    pub value: RpVariantValue<'a>,
88}
89
90impl<'a, F: 'static> RpVariantRef<'a, F>
91where
92    F: Flavor,
93{
94    /// Get the identifier for this variant.
95    pub fn ident(&self) -> &'a str {
96        self.ident.as_str()
97    }
98}
99
100impl<'a, F: 'static> fmt::Display for RpVariantRef<'a, F>
101where
102    F: Flavor,
103{
104    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
105        write!(fmt, "{} as {}", self.name, self.value)
106    }
107}
108
109/// Variant in an enum.
110#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
111#[serde(bound = "F::Package: Serialize, F::Name: Serialize, V: Serialize")]
112pub struct RpVariant<F: 'static, V>
113where
114    F: Flavor,
115{
116    pub name: F::Name,
117    pub ident: Loc<String>,
118    pub comment: Vec<String>,
119    pub value: V,
120}
121
122impl<'a, F: 'static, V> RpVariant<F, V>
123where
124    F: Flavor,
125{
126    /// Get the identifier of the variant.
127    pub fn ident(&self) -> &str {
128        self.ident.as_str()
129    }
130}
131
132impl<'a, F: 'static, V: 'a> RpVariant<F, V>
133where
134    F: Flavor,
135    RpVariantValue<'a>: From<&'a V>,
136{
137    /// Convert into a variant value.
138    pub fn value(&'a self) -> RpVariantValue<'a> {
139        RpVariantValue::from(&self.value)
140    }
141}
142
143impl<F: 'static, T, V> Translate<T> for RpVariant<F, V>
144where
145    F: Flavor,
146    T: Translator<Source = F>,
147{
148    type Source = F;
149    type Out = RpVariant<T::Target, V>;
150
151    /// Translate into different flavor.
152    fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpVariant<T::Target, V>> {
153        translator.visit(diag, &self.name)?;
154
155        let name = translator.translate_local_name(diag, RpReg::EnumVariant, self.name)?;
156
157        Ok(RpVariant {
158            name: name,
159            ident: self.ident,
160            comment: self.comment,
161            value: self.value,
162        })
163    }
164}
165
166/// Model for enum types
167#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
168pub enum RpEnumType {
169    String,
170    U32,
171    U64,
172    I32,
173    I64,
174}
175
176impl RpEnumType {
177    pub fn is_assignable_from(&self, value: &RpValue) -> bool {
178        use self::RpEnumType::*;
179
180        match (self, value) {
181            (&String, &RpValue::String(_)) => true,
182            (&U32, &RpValue::Number(_)) => true,
183            (&U64, &RpValue::Number(_)) => true,
184            (&I32, &RpValue::Number(_)) => true,
185            (&I64, &RpValue::Number(_)) => true,
186            _ => false,
187        }
188    }
189
190    /// Validate that the given number doesn't violate expected numeric bounds.
191    pub fn validate_number(&self, number: &RpNumber) -> Result<()> {
192        // max contiguous whole number that can be represented with a double: 2^53 - 1
193        const MAX_SAFE_INTEGER: i64 = 9007199254740991i64;
194        const MIN_SAFE_INTEGER: i64 = -9007199254740991i64;
195
196        use self::RpEnumType::*;
197
198        let (mn, mx): (BigInt, BigInt) = match *self {
199            String => return Err("expected number, got `string`".into()),
200            U32 => (0u32.into(), i32::max_value().into()),
201            U64 => (0u64.into(), MAX_SAFE_INTEGER.into()),
202            I32 => (i32::min_value().into(), i32::max_value().into()),
203            I64 => (MIN_SAFE_INTEGER.into(), MAX_SAFE_INTEGER.into()),
204        };
205
206        let n = number.to_bigint().ok_or_else(|| "not a whole number")?;
207
208        // withing bounds
209        if &mn <= n && n <= &mx {
210            return Ok(());
211        }
212
213        return Err(format!("number is not within {} to {} (inclusive)", mn, mx).into());
214    }
215}
216
217impl fmt::Display for RpEnumType {
218    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
219        use self::RpEnumType::*;
220
221        match *self {
222            String => "string".fmt(fmt),
223            U32 => "u32".fmt(fmt),
224            U64 => "u64".fmt(fmt),
225            I32 => "i32".fmt(fmt),
226            I64 => "i64".fmt(fmt),
227        }
228    }
229}
230
231#[derive(Debug, Clone, Serialize)]
232#[serde(bound = "F: Serialize, F::Package: Serialize, F::Name: Serialize")]
233#[serde(tag = "type", rename_all = "snake_case")]
234pub enum RpVariants<F: 'static>
235where
236    F: Flavor,
237{
238    String {
239        variants: Vec<Loc<RpVariant<F, String>>>,
240    },
241    Number {
242        variants: Vec<Loc<RpVariant<F, RpNumber>>>,
243    },
244}
245
246pub struct RpVariantsIter<'a, F: 'static>
247where
248    F: Flavor,
249{
250    iter: vec::IntoIter<RpVariantRef<'a, F>>,
251}
252
253impl<'a, F: 'static> Iterator for RpVariantsIter<'a, F>
254where
255    F: Flavor,
256{
257    type Item = RpVariantRef<'a, F>;
258
259    fn next(&mut self) -> Option<Self::Item> {
260        self.iter.next()
261    }
262}
263
264impl<F: 'static> RpVariants<F>
265where
266    F: Flavor,
267{
268    /// Iterate over all variants in a type-erasured manner.
269    pub fn iter(&self) -> RpVariantsIter<F> {
270        use self::RpVariants::*;
271
272        macro_rules! variants {
273            ($slf:ident, $($ty:ident),*) => {
274                match *$slf {
275                $(
276                $ty { ref variants } => {
277                    let mut __o = Vec::new();
278
279                    for v in variants {
280                        let (value, span) = Loc::borrow_pair(v);
281
282                        __o.push(RpVariantRef {
283                            span: span,
284                            name: &value.name,
285                            ident: &value.ident,
286                            comment: &value.comment,
287                            value: RpVariantValue::from(&value.value),
288                        })
289                    }
290
291                    __o
292                },
293                )*
294                }
295            };
296        }
297
298        let variants: Vec<_> = variants!(self, String, Number);
299
300        RpVariantsIter {
301            iter: variants.into_iter(),
302        }
303    }
304}
305
306impl<F: 'static, T> Translate<T> for RpVariants<F>
307where
308    F: Flavor,
309    T: Translator<Source = F>,
310{
311    type Source = F;
312    type Out = RpVariants<T::Target>;
313
314    /// Translate into different flavor.
315    fn translate(self, diag: &mut Diagnostics, translator: &T) -> Result<RpVariants<T::Target>> {
316        use self::RpVariants::*;
317
318        let out = match self {
319            String { variants } => String {
320                variants: variants.translate(diag, translator)?,
321            },
322            Number { variants } => Number {
323                variants: variants.translate(diag, translator)?,
324            },
325        };
326
327        Ok(out)
328    }
329}
330
331impl<'a, F: 'static> IntoIterator for &'a RpVariants<F>
332where
333    F: Flavor,
334{
335    type Item = RpVariantRef<'a, F>;
336    type IntoIter = RpVariantsIter<'a, F>;
337
338    fn into_iter(self) -> Self::IntoIter {
339        self.iter()
340    }
341}