1 #include <mbgl/style/expression/array_assertion.hpp>
2 #include <mbgl/style/expression/check_subtype.hpp>
3 #include <mbgl/util/string.hpp>
4 
5 namespace mbgl {
6 namespace style {
7 namespace expression {
8 
evaluate(const EvaluationContext & params) const9 EvaluationResult ArrayAssertion::evaluate(const EvaluationContext& params) const {
10     auto result = input->evaluate(params);
11     if (!result) {
12         return result.error();
13     }
14     type::Type expected = getType();
15     type::Type actual = typeOf(*result);
16     if (checkSubtype(expected, actual)) {
17         return EvaluationError {
18             "Expected value to be of type " + toString(expected) +
19             ", but found " + toString(actual) + " instead."
20         };
21     }
22     return *result;
23 }
24 
eachChild(const std::function<void (const Expression &)> & visit) const25 void ArrayAssertion::eachChild(const std::function<void(const Expression&)>& visit) const {
26     visit(*input);
27 }
28 
29 using namespace mbgl::style::conversion;
parse(const Convertible & value,ParsingContext & ctx)30 ParseResult ArrayAssertion::parse(const Convertible& value, ParsingContext& ctx) {
31 
32     static std::unordered_map<std::string, type::Type> itemTypes {
33         {"string", type::String},
34         {"number", type::Number},
35         {"boolean", type::Boolean}
36     };
37 
38     auto length = arrayLength(value);
39     if (length < 2 || length > 4) {
40         ctx.error("Expected 1, 2, or 3 arguments, but found " + util::toString(length - 1) + " instead.");
41         return ParseResult();
42     }
43 
44     optional<type::Type> itemType;
45     optional<std::size_t> N;
46     if (length > 2) {
47         optional<std::string> itemTypeName = toString(arrayMember(value, 1));
48         auto it = itemTypeName ? itemTypes.find(*itemTypeName) : itemTypes.end();
49         if (it == itemTypes.end()) {
50             ctx.error(
51                 R"(The item type argument of "array" must be one of string, number, boolean)",
52                 1
53             );
54             return ParseResult();
55         }
56         itemType = it->second;
57     } else {
58         itemType = {type::Value};
59     }
60 
61     if (length > 3) {
62         auto n = toNumber(arrayMember(value, 2));
63         if (!n || *n != std::floor(*n)) {
64             ctx.error(
65                 R"(The length argument to "array" must be a positive integer literal.)",
66                 2
67             );
68             return ParseResult();
69         }
70         N = optional<std::size_t>(*n);
71     }
72 
73     auto input = ctx.parse(arrayMember(value, length - 1), length - 1, {type::Value});
74     if (!input) {
75         return input;
76     }
77 
78     return ParseResult(std::make_unique<ArrayAssertion>(
79         type::Array(*itemType, N),
80         std::move(*input)
81     ));
82 }
83 
serialize() const84 mbgl::Value ArrayAssertion::serialize() const {
85     std::vector<mbgl::Value> serialized;
86     serialized.emplace_back(getOperator());
87 
88 
89     const auto array = getType().get<type::Array>();
90     if (array.itemType.is<type::StringType>()
91      || array.itemType.is<type::NumberType>()
92      || array.itemType.is<type::BooleanType>()) {
93         serialized.emplace_back(type::toString(array.itemType));
94         if (array.N) {
95             serialized.emplace_back(uint64_t(*array.N));
96         }
97     }
98 
99     serialized.emplace_back(input->serialize());
100     return serialized;
101 }
102 
103 } // namespace expression
104 } // namespace style
105 } // namespace mbgl
106