Backed out 5 changesets (bug 1890092, bug 1888683) for causing build bustages & crash...
[gecko.git] / third_party / rust / uniffi_udl / src / literal.rs
blob78f254425491ccc34ec5adc086f91f2da2142c3e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 use anyhow::{bail, Result};
6 use uniffi_meta::{LiteralMetadata, Radix, Type};
8 // We are able to use LiteralMetadata directly.
9 pub type Literal = LiteralMetadata;
11 // Convert weedle/udl syntax.
12 pub(super) fn convert_default_value(
13     default_value: &weedle::literal::DefaultValue<'_>,
14     type_: &Type,
15 ) -> Result<LiteralMetadata> {
16     fn convert_integer(literal: &weedle::literal::IntegerLit<'_>, type_: &Type) -> Result<Literal> {
17         let (string, radix) = match literal {
18             weedle::literal::IntegerLit::Dec(v) => (v.0, Radix::Decimal),
19             weedle::literal::IntegerLit::Hex(v) => (v.0, Radix::Hexadecimal),
20             weedle::literal::IntegerLit::Oct(v) => (v.0, Radix::Octal),
21         };
22         // This is the radix of the parsed number, passed to `from_str_radix`.
23         let src_radix = radix as u32;
24         // This radix tells the backends how to represent the number in the output languages.
25         let dest_radix = if string == "0" || string.starts_with('-') {
26             // 1. weedle parses "0" as an octal literal, but we most likely want to treat this as a decimal.
27             // 2. Explicitly negatively signed hex numbers won't convert via i64 very well if they're not 64 bit.
28             //    For ease of implementation, output will use decimal.
29             Radix::Decimal
30         } else {
31             radix
32         };
34         // Clippy seems to think we should be using `strip_prefix` here, but
35         // it seems confused as to what this is actually doing.
36         #[allow(clippy::manual_strip)]
37         let string = if string.starts_with('-') {
38             ("-".to_string() + string[1..].trim_start_matches("0x")).to_lowercase()
39         } else {
40             string.trim_start_matches("0x").to_lowercase()
41         };
43         Ok(match type_ {
44             Type::Int8 | Type::Int16 | Type::Int32 | Type::Int64 => Literal::Int(
45                 i64::from_str_radix(&string, src_radix)?,
46                 dest_radix,
47                 type_.clone(),
48             ),
49             Type::UInt8 | Type::UInt16 | Type::UInt32 | Type::UInt64 => Literal::UInt(
50                 u64::from_str_radix(&string, src_radix)?,
51                 dest_radix,
52                 type_.clone(),
53             ),
55             _ => bail!("Cannot coerce literal {} into a non-integer type", string),
56         })
57     }
59     fn convert_float(literal: &weedle::literal::FloatLit<'_>, type_: &Type) -> Result<Literal> {
60         let string = match literal {
61             weedle::literal::FloatLit::Value(v) => v.0,
63             _ => bail!("Infinity and NaN is not currently supported"),
64         };
66         Ok(match type_ {
67             Type::Float32 | Type::Float64 => Literal::Float(string.to_string(), type_.clone()),
68             _ => bail!("Cannot coerce literal {} into a non-float type", string),
69         })
70     }
72     Ok(match (default_value, type_) {
73         (weedle::literal::DefaultValue::Boolean(b), Type::Boolean) => Literal::Boolean(b.0),
74         (weedle::literal::DefaultValue::String(s), Type::String) => {
75             // Note that weedle doesn't parse escaped double quotes.
76             // Keeping backends using double quotes (possible for all to date)
77             // means we don't need to escape single quotes. But we haven't spent a lot of time
78             // trying to break default values with weird escapes and quotes.
79             Literal::String(s.0.to_string())
80         }
81         (weedle::literal::DefaultValue::EmptyArray(_), Type::Sequence { .. }) => {
82             Literal::EmptySequence
83         }
84         (weedle::literal::DefaultValue::String(s), Type::Enum { .. }) => {
85             Literal::Enum(s.0.to_string(), type_.clone())
86         }
87         (weedle::literal::DefaultValue::Null(_), Type::Optional { .. }) => Literal::Null,
88         (_, Type::Optional { inner_type, .. }) => convert_default_value(default_value, inner_type)?,
90         // We'll ensure the type safety in the convert_* number methods.
91         (weedle::literal::DefaultValue::Integer(i), _) => convert_integer(i, type_)?,
92         (weedle::literal::DefaultValue::Float(i), _) => convert_float(i, type_)?,
94         _ => bail!("No support for {:?} literal yet", default_value),
95     })
98 #[cfg(test)]
99 mod test {
100     use super::*;
101     use weedle::Parse;
103     fn parse_and_convert(expr: &str, t: Type) -> Result<Literal> {
104         let (_, node) = weedle::literal::DefaultValue::parse(expr).unwrap();
105         convert_default_value(&node, &t)
106     }
108     #[test]
109     fn test_default_value_conversion() -> Result<()> {
110         assert!(matches!(
111             parse_and_convert("0", Type::UInt8)?,
112             Literal::UInt(0, Radix::Decimal, Type::UInt8)
113         ));
114         assert!(matches!(
115             parse_and_convert("-12", Type::Int32)?,
116             Literal::Int(-12, Radix::Decimal, Type::Int32)
117         ));
118         assert!(
119             matches!(parse_and_convert("3.14", Type::Float32)?, Literal::Float(v, Type::Float32) if v == "3.14")
120         );
121         assert!(matches!(
122             parse_and_convert("false", Type::Boolean)?,
123             Literal::Boolean(false)
124         ));
125         assert!(
126             matches!(parse_and_convert("\"TEST\"", Type::String)?, Literal::String(v) if v == "TEST")
127         );
128         assert!(
129             matches!(parse_and_convert("\"one\"", Type::Enum { name: "E".into(), module_path: "".into() })?, Literal::Enum(v, Type::Enum { name, .. }) if v == "one" && name == "E")
130         );
131         assert!(matches!(
132             parse_and_convert(
133                 "[]",
134                 Type::Sequence {
135                     inner_type: Box::new(Type::String)
136                 }
137             )?,
138             Literal::EmptySequence
139         ));
140         assert!(matches!(
141             parse_and_convert(
142                 "null",
143                 Type::Optional {
144                     inner_type: Box::new(Type::String)
145                 }
146             )?,
147             Literal::Null
148         ));
149         Ok(())
150     }
151     #[test]
152     fn test_error_on_type_mismatch() {
153         assert_eq!(
154             parse_and_convert("0", Type::Boolean)
155                 .unwrap_err()
156                 .to_string(),
157             "Cannot coerce literal 0 into a non-integer type"
158         );
159         assert!(parse_and_convert("{}", Type::Boolean)
160             .unwrap_err()
161             .to_string()
162             .starts_with("No support for"));
163     }