diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2017-03-07 17:00:53 -0800 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2017-03-23 13:31:13 -0700 |
commit | d7227e13a7a87cf50a4c8c1f0615fc565f5a2679 (patch) | |
tree | eda76a2da3220f3cfeec901400369cf9c8361f58 | |
parent | 1c757cce34344dfecc9a724034680225143f92b7 (diff) | |
download | qtlocation-mapboxgl-d7227e13a7a87cf50a4c8c1f0615fc565f5a2679.tar.gz |
[all] Replace Result<T> with optional<T> plus out Error parameter
36 files changed, 550 insertions, 486 deletions
diff --git a/benchmark/parse/filter.benchmark.cpp b/benchmark/parse/filter.benchmark.cpp index 3918f03539..d650cb72c9 100644 --- a/benchmark/parse/filter.benchmark.cpp +++ b/benchmark/parse/filter.benchmark.cpp @@ -14,7 +14,8 @@ using namespace mbgl; style::Filter parse(const char* expression) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.Parse<0>(expression); - return *style::conversion::convert<style::Filter, JSValue>(doc); + style::conversion::Error error; + return *style::conversion::convert<style::Filter, JSValue>(doc, error); } static void Parse_Filter(benchmark::State& state) { diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 45a3ed25b3..e98f999116 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -276,8 +276,6 @@ set(MBGL_CORE_FILES src/mbgl/style/possibly_evaluated_property_value.hpp src/mbgl/style/property_evaluation_parameters.hpp src/mbgl/style/property_evaluator.hpp - src/mbgl/style/property_parsing.cpp - src/mbgl/style/property_parsing.hpp src/mbgl/style/rapidjson_conversion.hpp src/mbgl/style/source.cpp src/mbgl/style/source_impl.cpp diff --git a/include/mbgl/style/conversion.hpp b/include/mbgl/style/conversion.hpp index e53adcb942..d6fb3a6dd0 100644 --- a/include/mbgl/style/conversion.hpp +++ b/include/mbgl/style/conversion.hpp @@ -1,6 +1,6 @@ #pragma once -#include <mbgl/util/variant.hpp> +#include <mbgl/util/optional.hpp> #include <string> @@ -21,11 +21,11 @@ namespace conversion { A single template function serves as the public interface: template <class T, class V> - Result<T> convert(const V& value); + optional<T> convert(const V& value, Error& error); - Where `T` is one of the above types. If the conversion fails, the `Error` variant of `Result` is - returned, which includes diagnostic text suitable for presentation to a library user. Otherwise, - the `T` variant of `Result` is returned. + Where `T` is one of the above types. If the conversion fails, the result is empty, and the + error parameter includes diagnostic text suitable for presentation to a library user. Otherwise, + a filled optional is returned. The implementation of `convert` requires that the following are legal expressions for a value `v` of type `const V&`: @@ -57,37 +57,12 @@ namespace conversion { struct Error { std::string message; }; -template <class T> -class Result : private variant<T, Error> { -public: - using variant<T, Error>::variant; - - explicit operator bool() const { - return this->template is<T>(); - } - - T& operator*() { - assert(this->template is<T>()); - return this->template get<T>(); - } - - const T& operator*() const { - assert(this->template is<T>()); - return this->template get<T>(); - } - - const Error& error() const { - assert(this->template is<Error>()); - return this->template get<Error>(); - } -}; - template <class T, class Enable = void> struct Converter; template <class T, class V, class...Args> -Result<T> convert(const V& value, Args&&...args) { - return Converter<T>()(value, std::forward<Args>(args)...); +optional<T> convert(const V& value, Error& error, Args&&...args) { + return Converter<T>()(value, error, std::forward<Args>(args)...); } } // namespace conversion diff --git a/include/mbgl/style/conversion/constant.hpp b/include/mbgl/style/conversion/constant.hpp index 05bf968f4d..1e1fdc2ee8 100644 --- a/include/mbgl/style/conversion/constant.hpp +++ b/include/mbgl/style/conversion/constant.hpp @@ -16,10 +16,11 @@ namespace conversion { template <> struct Converter<bool> { template <class V> - Result<bool> operator()(const V& value) const { + optional<bool> operator()(const V& value, Error& error) const { optional<bool> converted = toBool(value); if (!converted) { - return Error { "value must be a boolean" }; + error = { "value must be a boolean" }; + return {}; } return *converted; } @@ -28,10 +29,11 @@ struct Converter<bool> { template <> struct Converter<float> { template <class V> - Result<float> operator()(const V& value) const { + optional<float> operator()(const V& value, Error& error) const { optional<float> converted = toNumber(value); if (!converted) { - return Error { "value must be a number" }; + error = { "value must be a number" }; + return {}; } return *converted; } @@ -40,10 +42,11 @@ struct Converter<float> { template <> struct Converter<std::string> { template <class V> - Result<std::string> operator()(const V& value) const { + optional<std::string> operator()(const V& value, Error& error) const { optional<std::string> converted = toString(value); if (!converted) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } return *converted; } @@ -52,15 +55,17 @@ struct Converter<std::string> { template <class T> struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { template <class V> - Result<T> operator()(const V& value) const { + optional<T> operator()(const V& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } const auto result = Enum<T>::toEnum(*string); if (!result) { - return Error { "value must be a valid enumeration value" }; + error = { "value must be a valid enumeration value" }; + return {}; } return *result; @@ -70,15 +75,17 @@ struct Converter<T, typename std::enable_if_t<std::is_enum<T>::value>> { template <> struct Converter<Color> { template <class V> - Result<Color> operator()(const V& value) const { + optional<Color> operator()(const V& value, Error& error) const { optional<std::string> string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } optional<Color> color = Color::parse(*string); if (!color) { - return Error { "value must be a valid color" }; + error = { "value must be a valid color" }; + return {}; } return *color; @@ -88,15 +95,17 @@ struct Converter<Color> { template <> struct Converter<std::array<float, 2>> { template <class V> - Result<std::array<float, 2>> operator()(const V& value) const { + optional<std::array<float, 2>> operator()(const V& value, Error& error) const { if (!isArray(value) || arrayLength(value) != 2) { - return Error { "value must be an array of two numbers" }; + error = { "value must be an array of two numbers" }; + return {}; } optional<float> first = toNumber(arrayMember(value, 0)); optional<float> second = toNumber(arrayMember(value, 1)); if (!first || !second) { - return Error { "value must be an array of two numbers" }; + error = { "value must be an array of two numbers" }; + return {}; } return std::array<float, 2> {{ *first, *second }}; @@ -106,9 +115,10 @@ struct Converter<std::array<float, 2>> { template <> struct Converter<std::array<float, 4>> { template <class V> - Result<std::array<float, 4>> operator()(const V& value) const { + optional<std::array<float, 4>> operator()(const V& value, Error& error) const { if (!isArray(value) || arrayLength(value) != 4) { - return Error { "value must be an array of four numbers" }; + error = { "value must be an array of four numbers" }; + return {}; } optional<float> first = toNumber(arrayMember(value, 0)); @@ -116,7 +126,8 @@ struct Converter<std::array<float, 4>> { optional<float> third = toNumber(arrayMember(value, 2)); optional<float> fourth = toNumber(arrayMember(value, 3)); if (!first || !second) { - return Error { "value must be an array of four numbers" }; + error = { "value must be an array of four numbers" }; + return {}; } return std::array<float, 4> {{ *first, *second, *third, *fourth }}; @@ -126,9 +137,10 @@ struct Converter<std::array<float, 4>> { template <> struct Converter<std::vector<float>> { template <class V> - Result<std::vector<float>> operator()(const V& value) const { + optional<std::vector<float>> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "value must be an array" }; + error = { "value must be an array" }; + return {}; } std::vector<float> result; @@ -137,7 +149,8 @@ struct Converter<std::vector<float>> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional<float> number = toNumber(arrayMember(value, i)); if (!number) { - return Error { "value must be an array of numbers" }; + error = { "value must be an array of numbers" }; + return {}; } result.push_back(*number); } @@ -149,9 +162,10 @@ struct Converter<std::vector<float>> { template <> struct Converter<std::vector<std::string>> { template <class V> - Result<std::vector<std::string>> operator()(const V& value) const { + optional<std::vector<std::string>> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "value must be an array" }; + error = { "value must be an array" }; + return {}; } std::vector<std::string> result; @@ -160,7 +174,8 @@ struct Converter<std::vector<std::string>> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional<std::string> string = toString(arrayMember(value, i)); if (!string) { - return Error { "value must be an array of strings" }; + error = { "value must be an array of strings" }; + return {}; } result.push_back(*string); } diff --git a/include/mbgl/style/conversion/data_driven_property_value.hpp b/include/mbgl/style/conversion/data_driven_property_value.hpp index 83f44fdb27..79b15dcfb0 100644 --- a/include/mbgl/style/conversion/data_driven_property_value.hpp +++ b/include/mbgl/style/conversion/data_driven_property_value.hpp @@ -12,29 +12,29 @@ namespace conversion { template <class T> struct Converter<DataDrivenPropertyValue<T>> { template <class V> - Result<DataDrivenPropertyValue<T>> operator()(const V& value) const { + optional<DataDrivenPropertyValue<T>> operator()(const V& value, Error& error) const { if (isUndefined(value)) { - return {}; + return DataDrivenPropertyValue<T>(); } else if (!isObject(value)) { - Result<T> constant = convert<T>(value); + optional<T> constant = convert<T>(value, error); if (!constant) { - return constant.error(); + return {}; } return DataDrivenPropertyValue<T>(*constant); } else if (!objectMember(value, "property")) { - Result<CameraFunction<T>> function = convert<CameraFunction<T>>(value); + optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error); if (!function) { - return function.error(); + return {}; } return DataDrivenPropertyValue<T>(*function); } else { - Result<CompositeFunction<T>> composite = convert<CompositeFunction<T>>(value); + optional<CompositeFunction<T>> composite = convert<CompositeFunction<T>>(value, error); if (composite) { return DataDrivenPropertyValue<T>(*composite); } - Result<SourceFunction<T>> source = convert<SourceFunction<T>>(value); + optional<SourceFunction<T>> source = convert<SourceFunction<T>>(value, error); if (!source) { - return source.error(); + return {}; } return DataDrivenPropertyValue<T>(*source); } diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp index 2c4eeb4ac7..1f8f0fd161 100644 --- a/include/mbgl/style/conversion/filter.hpp +++ b/include/mbgl/style/conversion/filter.hpp @@ -12,65 +12,71 @@ template <> struct Converter<Filter> { public: template <class V> - Result<Filter> operator()(const V& value) const { + optional<Filter> operator()(const V& value, Error& error) const { if (!isArray(value)) { - return Error { "filter expression must be an array" }; + error = { "filter expression must be an array" }; + return {}; } if (arrayLength(value) < 1) { - return Error { "filter expression must have at least 1 element" }; + error = { "filter expression must have at least 1 element" }; + return {}; } optional<std::string> op = toString(arrayMember(value, 0)); if (!op) { - return Error { "filter operator must be a string" }; + error = { "filter operator must be a string" }; + return {}; } if (*op == "==") { - return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value); + return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value, error); } else if (*op == "!=") { - return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value); + return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value, error); } else if (*op == ">") { - return convertBinaryFilter<GreaterThanFilter>(value); + return convertBinaryFilter<GreaterThanFilter>(value, error); } else if (*op == ">=") { - return convertBinaryFilter<GreaterThanEqualsFilter>(value); + return convertBinaryFilter<GreaterThanEqualsFilter>(value, error); } else if (*op == "<") { - return convertBinaryFilter<LessThanFilter>(value); + return convertBinaryFilter<LessThanFilter>(value, error); } else if (*op == "<=") { - return convertBinaryFilter<LessThanEqualsFilter>(value); + return convertBinaryFilter<LessThanEqualsFilter>(value, error); } else if (*op == "in") { - return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value); + return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value, error); } else if (*op == "!in") { - return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value); + return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value, error); } else if (*op == "all") { - return convertCompoundFilter<AllFilter>(value); + return convertCompoundFilter<AllFilter>(value, error); } else if (*op == "any") { - return convertCompoundFilter<AnyFilter>(value); + return convertCompoundFilter<AnyFilter>(value, error); } else if (*op == "none") { - return convertCompoundFilter<NoneFilter>(value); + return convertCompoundFilter<NoneFilter>(value, error); } else if (*op == "has") { - return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value); + return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value, error); } else if (*op == "!has") { - return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value); + return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value, error); } - return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" }; + error = { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" }; + return {}; } private: - Result<Value> normalizeValue(const optional<Value>& value) const { + optional<Value> normalizeValue(const optional<Value>& value, Error& error) const { if (!value) { - return Error { "filter expression value must be a boolean, number, or string" }; + error = { "filter expression value must be a boolean, number, or string" }; + return {}; } else { return *value; } } template <class V> - Result<FeatureType> toFeatureType(const V& value) const { + optional<FeatureType> toFeatureType(const V& value, Error& error) const { optional<std::string> type = toString(value); if (!type) { - return Error { "value for $type filter must be a string" }; + error = { "value for $type filter must be a string" }; + return {}; } else if (*type == "Point") { return FeatureType::Point; } else if (*type == "LineString") { @@ -78,162 +84,173 @@ private: } else if (*type == "Polygon") { return FeatureType::Polygon; } else { - return Error { "value for $type filter must be Point, LineString, or Polygon" }; + error = { "value for $type filter must be Point, LineString, or Polygon" }; + return {}; } } template <class V> - Result<FeatureIdentifier> toFeatureIdentifier(const V& value) const { + optional<FeatureIdentifier> toFeatureIdentifier(const V& value, Error& error) const { optional<Value> identifier = toValue(value); if (!identifier) { - return Error { "filter expression value must be a boolean, number, or string" }; + error = { "filter expression value must be a boolean, number, or string" }; + return {}; } else { return (*identifier).match( - [] (uint64_t t) -> Result<FeatureIdentifier> { return t; }, - [] ( int64_t t) -> Result<FeatureIdentifier> { return t; }, - [] ( double t) -> Result<FeatureIdentifier> { return t; }, - [] (const std::string& t) -> Result<FeatureIdentifier> { return t; }, - [] (const auto&) -> Result<FeatureIdentifier> { - return Error { "filter expression value must be a boolean, number, or string" }; + [] (uint64_t t) -> optional<FeatureIdentifier> { return { t }; }, + [] ( int64_t t) -> optional<FeatureIdentifier> { return { t }; }, + [] ( double t) -> optional<FeatureIdentifier> { return { t }; }, + [] (const std::string& t) -> optional<FeatureIdentifier> { return { t }; }, + [&] (const auto&) -> optional<FeatureIdentifier> { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; }); } } template <class FilterType, class IdentifierFilterType, class V> - Result<Filter> convertUnaryFilter(const V& value) const { + optional<Filter> convertUnaryFilter(const V& value, Error& error) const { if (arrayLength(value) < 2) { - return Error { "filter expression must have 2 elements" }; + error = { "filter expression must have 2 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } if (*key == "$id") { - return IdentifierFilterType {}; + return { IdentifierFilterType {} }; } else { - return FilterType { *key }; + return { FilterType { *key } }; } } template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V> - Result<Filter> convertEqualityFilter(const V& value) const { + optional<Filter> convertEqualityFilter(const V& value, Error& error) const { if (arrayLength(value) < 3) { - return Error { "filter expression must have 3 elements" }; + error = { "filter expression must have 3 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } if (*key == "$type") { - Result<FeatureType> filterValue = toFeatureType(arrayMember(value, 2)); + optional<FeatureType> filterValue = toFeatureType(arrayMember(value, 2), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return TypeFilterType { *filterValue }; + return { TypeFilterType { *filterValue } }; } else if (*key == "$id") { - Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2)); + optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return IdentifierFilterType { *filterValue }; + return { IdentifierFilterType { *filterValue } }; } else { - Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2))); + optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return FilterType { *key, *filterValue }; + return { FilterType { *key, *filterValue } }; } } template <class FilterType, class V> - Result<Filter> convertBinaryFilter(const V& value) const { + optional<Filter> convertBinaryFilter(const V& value, Error& error) const { if (arrayLength(value) < 3) { - return Error { "filter expression must have 3 elements" }; + error = { "filter expression must have 3 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } - Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2))); + optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return FilterType { *key, *filterValue }; + return { FilterType { *key, *filterValue } }; } template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V> - Result<Filter> convertSetFilter(const V& value) const { + optional<Filter> convertSetFilter(const V& value, Error& error) const { if (arrayLength(value) < 2) { - return Error { "filter expression must at least 2 elements" }; + error = { "filter expression must at least 2 elements" }; + return {}; } optional<std::string> key = toString(arrayMember(value, 1)); if (!key) { - return Error { "filter expression key must be a string" }; + error = { "filter expression key must be a string" }; + return {}; } if (*key == "$type") { std::vector<FeatureType> values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result<FeatureType> filterValue = toFeatureType(arrayMember(value, i)); + optional<FeatureType> filterValue = toFeatureType(arrayMember(value, i), error); if (!filterValue) { - return filterValue.error(); + return {}; } values.push_back(*filterValue); } - return TypeFilterType { std::move(values) }; + return { TypeFilterType { std::move(values) } }; } else if (*key == "$id") { std::vector<FeatureIdentifier> values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i)); + optional<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i), error); if (!filterValue) { - return filterValue.error(); + return {}; } values.push_back(*filterValue); } - return IdentifierFilterType { std::move(values) }; + return { IdentifierFilterType { std::move(values) } }; } else { std::vector<Value> values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, i))); + optional<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)), error); if (!filterValue) { - return filterValue.error(); + return {}; } values.push_back(*filterValue); } - return FilterType { *key, std::move(values) }; + return { FilterType { *key, std::move(values) } }; } } template <class FilterType, class V> - Result<Filter> convertCompoundFilter(const V& value) const { + optional<Filter> convertCompoundFilter(const V& value, Error& error) const { std::vector<Filter> filters; for (std::size_t i = 1; i < arrayLength(value); ++i) { - Result<Filter> element = operator()(arrayMember(value, i)); + optional<Filter> element = operator()(arrayMember(value, i), error); if (!element) { - return element.error(); + return {}; } filters.push_back(*element); } - return FilterType { std::move(filters) }; + return { FilterType { std::move(filters) } }; } }; diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index a5979e6799..fa8af1e2be 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -12,18 +12,21 @@ namespace style { namespace conversion { template <class D, class R, class V> -Result<std::map<D, R>> convertStops(const V& value) { +optional<std::map<D, R>> convertStops(const V& value, Error& error) { auto stopsValue = objectMember(value, "stops"); if (!stopsValue) { - return Error { "function value must specify stops" }; + error = { "function value must specify stops" }; + return {}; } if (!isArray(*stopsValue)) { - return Error { "function stops must be an array" }; + error = { "function stops must be an array" }; + return {}; } if (arrayLength(*stopsValue) == 0) { - return Error { "function must have at least one stop" }; + error = { "function must have at least one stop" }; + return {}; } std::map<D, R> stops; @@ -31,21 +34,23 @@ Result<std::map<D, R>> convertStops(const V& value) { const auto& stopValue = arrayMember(*stopsValue, i); if (!isArray(stopValue)) { - return Error { "function stop must be an array" }; + error = { "function stop must be an array" }; + return {}; } if (arrayLength(stopValue) != 2) { - return Error { "function stop must have two elements" }; + error = { "function stop must have two elements" }; + return {}; } - Result<D> d = convert<D>(arrayMember(stopValue, 0)); + optional<D> d = convert<D>(arrayMember(stopValue, 0), error); if (!d) { - return d.error(); + return {}; } - Result<R> r = convert<R>(arrayMember(stopValue, 1)); + optional<R> r = convert<R>(arrayMember(stopValue, 1), error); if (!r) { - return r.error(); + return {}; } stops.emplace(*d, *r); @@ -59,10 +64,10 @@ struct Converter<ExponentialStops<T>> { static constexpr const char * type = "exponential"; template <class V> - Result<ExponentialStops<T>> operator()(const V& value) const { - auto stops = convertStops<float, T>(value); + optional<ExponentialStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<float, T>(value, error); if (!stops) { - return stops.error(); + return {}; } auto baseValue = objectMember(value, "base"); @@ -72,7 +77,8 @@ struct Converter<ExponentialStops<T>> { optional<float> base = toNumber(*baseValue); if (!base) { - return Error { "function base must be a number"}; + error = { "function base must be a number"}; + return {}; } return ExponentialStops<T>(*stops, *base); @@ -84,10 +90,10 @@ struct Converter<IntervalStops<T>> { static constexpr const char * type = "interval"; template <class V> - Result<IntervalStops<T>> operator()(const V& value) const { - auto stops = convertStops<float, T>(value); + optional<IntervalStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<float, T>(value, error); if (!stops) { - return stops.error(); + return {}; } return IntervalStops<T>(*stops); } @@ -96,23 +102,24 @@ struct Converter<IntervalStops<T>> { template <> struct Converter<CategoricalValue> { template <class V> - Result<CategoricalValue> operator()(const V& value) const { + optional<CategoricalValue> operator()(const V& value, Error& error) const { auto b = toBool(value); if (b) { - return *b; + return { *b }; } auto n = toNumber(value); if (n) { - return int64_t(*n); + return { int64_t(*n) }; } auto s = toString(value); if (s) { - return *s; + return { *s }; } - return Error { "stop domain value must be a number, string, or boolean" }; + error = { "stop domain value must be a number, string, or boolean" }; + return {}; } }; @@ -121,10 +128,10 @@ struct Converter<CategoricalStops<T>> { static constexpr const char * type = "categorical"; template <class V> - Result<CategoricalStops<T>> operator()(const V& value) const { - auto stops = convertStops<CategoricalValue, T>(value); + optional<CategoricalStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<CategoricalValue, T>(value, error); if (!stops) { - return stops.error(); + return {}; } return CategoricalStops<T>( std::map<CategoricalValue, T>((*stops).begin(), (*stops).end())); @@ -136,7 +143,7 @@ struct Converter<IdentityStops<T>> { static constexpr const char * type = "identity"; template <class V> - Result<IdentityStops<T>> operator()(const V&) const { + optional<IdentityStops<T>> operator()(const V&, Error&) const { return IdentityStops<T>(); } }; @@ -148,7 +155,7 @@ template <class T, class... Ts> struct StopsConverter<T, variant<Ts...>> { public: template <class V> - Result<variant<Ts...>> operator()(const V& value) const { + optional<variant<Ts...>> operator()(const V& value, Error& error) const { std::string type = util::Interpolatable<T> ? "exponential" : "interval"; auto typeValue = objectMember(value, "type"); @@ -156,16 +163,18 @@ public: type = *toString(*typeValue); } - optional<Result<variant<Ts...>>> result; + bool matched = false; + optional<variant<Ts...>> result; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 auto tryConvert = [&] (auto* tp) { using Stops = std::decay_t<decltype(*tp)>; if (type == Converter<Stops>::type) { - auto stops = convert<Stops>(value); - result = stops - ? Result<variant<Ts...>>(*stops) - : Result<variant<Ts...>>(stops.error()); + matched = true; + optional<Stops> stops = convert<Stops>(value, error); + if (stops) { + result = variant<Ts...>(*stops); + } } }; @@ -173,25 +182,27 @@ public: (tryConvert((Ts*)nullptr), 0)... }); - if (!result) { - return Error { "unsupported function type" }; + if (!matched) { + error = { "unsupported function type" }; + return {}; } - return *result; + return result; } }; template <class T> struct Converter<CameraFunction<T>> { template <class V> - Result<CameraFunction<T>> operator()(const V& value) const { + optional<CameraFunction<T>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "function must be an object" }; + error = { "function must be an object" }; + return {}; } - auto stops = StopsConverter<T, typename CameraFunction<T>::Stops>()(value); + auto stops = StopsConverter<T, typename CameraFunction<T>::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } return CameraFunction<T>(*stops); @@ -199,46 +210,50 @@ struct Converter<CameraFunction<T>> { }; template <class T, class V> -Result<optional<T>> convertDefaultValue(const V& value) { +optional<optional<T>> convertDefaultValue(const V& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); if (!defaultValueValue) { - return {}; + return optional<T>(); } - auto defaultValue = convert<T>(*defaultValueValue); + auto defaultValue = convert<T>(*defaultValueValue, error); if (!defaultValue) { - return Error { "wrong type for \"default\": " + defaultValue.error().message }; + error = { "wrong type for \"default\": " + error.message }; + return {}; } - return *defaultValue; + return { *defaultValue }; } template <class T> struct Converter<SourceFunction<T>> { template <class V> - Result<SourceFunction<T>> operator()(const V& value) const { + optional<SourceFunction<T>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "function must be an object" }; + error = { "function must be an object" }; + return {}; } auto propertyValue = objectMember(value, "property"); if (!propertyValue) { - return Error { "function must specify property" }; + error = { "function must specify property" }; + return {}; } auto propertyString = toString(*propertyValue); if (!propertyString) { - return Error { "function property must be a string" }; + error = { "function property must be a string" }; + return {}; } - auto stops = StopsConverter<T, typename SourceFunction<T>::Stops>()(value); + auto stops = StopsConverter<T, typename SourceFunction<T>::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } - auto defaultValue = convertDefaultValue<T>(value); + auto defaultValue = convertDefaultValue<T>(value, error); if (!defaultValue) { - return defaultValue.error(); + return {}; } return SourceFunction<T>(*propertyString, *stops, *defaultValue); @@ -253,29 +268,32 @@ struct CompositeValue : std::pair<float, S> { template <class S> struct Converter<CompositeValue<S>> { template <class V> - Result<CompositeValue<S>> operator()(const V& value) const { + optional<CompositeValue<S>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "stop must be an object" }; + error = { "stop must be an object" }; + return {}; } auto zoomValue = objectMember(value, "zoom"); if (!zoomValue) { - return Error { "stop must specify zoom" }; + error = { "stop must specify zoom" }; + return {}; } auto propertyValue = objectMember(value, "value"); if (!propertyValue) { - return Error { "stop must specify value" }; + error = { "stop must specify value" }; + return {}; } - Result<float> z = convert<float>(*zoomValue); + optional<float> z = convert<float>(*zoomValue, error); if (!z) { - return z.error(); + return {}; } - Result<S> s = convert<S>(*propertyValue); + optional<S> s = convert<S>(*propertyValue, error); if (!s) { - return s.error(); + return {}; } return CompositeValue<S> { *z, *s }; @@ -287,10 +305,10 @@ struct Converter<CompositeExponentialStops<T>> { static constexpr const char * type = "exponential"; template <class V> - Result<CompositeExponentialStops<T>> operator()(const V& value) const { - auto stops = convertStops<CompositeValue<float>, T>(value); + optional<CompositeExponentialStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<CompositeValue<float>, T>(value, error); if (!stops) { - return stops.error(); + return {}; } auto base = 1.0f; @@ -313,10 +331,10 @@ struct Converter<CompositeIntervalStops<T>> { static constexpr const char * type = "interval"; template <class V> - Result<CompositeIntervalStops<T>> operator()(const V& value) const { - auto stops = convertStops<CompositeValue<float>, T>(value); + optional<CompositeIntervalStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<CompositeValue<float>, T>(value, error); if (!stops) { - return stops.error(); + return {}; } std::map<float, std::map<float, T>> convertedStops; @@ -333,10 +351,10 @@ struct Converter<CompositeCategoricalStops<T>> { static constexpr const char * type = "categorical"; template <class V> - Result<CompositeCategoricalStops<T>> operator()(const V& value) const { - auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value); + optional<CompositeCategoricalStops<T>> operator()(const V& value, Error& error) const { + auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error); if (!stops) { - return stops.error(); + return {}; } std::map<float, std::map<CategoricalValue, T>> convertedStops; @@ -351,29 +369,32 @@ struct Converter<CompositeCategoricalStops<T>> { template <class T> struct Converter<CompositeFunction<T>> { template <class V> - Result<CompositeFunction<T>> operator()(const V& value) const { + optional<CompositeFunction<T>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "function must be an object" }; + error = { "function must be an object" }; + return {}; } auto propertyValue = objectMember(value, "property"); if (!propertyValue) { - return Error { "function must specify property" }; + error = { "function must specify property" }; + return {}; } auto propertyString = toString(*propertyValue); if (!propertyString) { - return Error { "function property must be a string" }; + error = { "function property must be a string" }; + return {}; } - auto stops = StopsConverter<T, typename CompositeFunction<T>::Stops>()(value); + auto stops = StopsConverter<T, typename CompositeFunction<T>::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } - auto defaultValue = convertDefaultValue<T>(value); + auto defaultValue = convertDefaultValue<T>(value, error); if (!defaultValue) { - return defaultValue.error(); + return {}; } return CompositeFunction<T>(*propertyString, *stops, *defaultValue); diff --git a/include/mbgl/style/conversion/geojson.hpp b/include/mbgl/style/conversion/geojson.hpp index ba10b3ecc8..6e6e97dbc0 100644 --- a/include/mbgl/style/conversion/geojson.hpp +++ b/include/mbgl/style/conversion/geojson.hpp @@ -8,7 +8,7 @@ namespace style { namespace conversion { template <class V> -Result<GeoJSON> convertGeoJSON(const V& value); +optional<GeoJSON> convertGeoJSON(const V& value, Error& error); } // namespace conversion } // namespace style diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 880090b402..19383d90ce 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -11,7 +11,7 @@ template <> struct Converter<GeoJSONOptions> { template <class V> - Result<GeoJSONOptions> operator()(const V& value) const { + optional<GeoJSONOptions> operator()(const V& value, Error& error) const { GeoJSONOptions options; const auto maxzoomValue = objectMember(value, "maxzoom"); @@ -19,7 +19,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*maxzoomValue)) { options.maxzoom = static_cast<uint8_t>(*toNumber(*maxzoomValue)); } else { - return Error{ "GeoJSON source maxzoom value must be a number" }; + error = { "GeoJSON source maxzoom value must be a number" }; + return {}; } } @@ -28,7 +29,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*bufferValue)) { options.buffer = static_cast<uint16_t>(*toNumber(*bufferValue)); } else { - return Error{ "GeoJSON source buffer value must be a number" }; + error = { "GeoJSON source buffer value must be a number" }; + return {}; } } @@ -37,7 +39,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*toleranceValue)) { options.tolerance = static_cast<double>(*toNumber(*toleranceValue)); } else { - return Error{ "GeoJSON source tolerance value must be a number" }; + error = { "GeoJSON source tolerance value must be a number" }; + return {}; } } @@ -46,7 +49,8 @@ struct Converter<GeoJSONOptions> { if (toBool(*clusterValue)) { options.cluster = *toBool(*clusterValue); } else { - return Error{ "GeoJSON source cluster value must be a boolean" }; + error = { "GeoJSON source cluster value must be a boolean" }; + return {}; } } @@ -55,7 +59,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*clusterMaxZoomValue)) { options.clusterMaxZoom = static_cast<uint8_t>(*toNumber(*clusterMaxZoomValue)); } else { - return Error{ "GeoJSON source clusterMaxZoom value must be a number" }; + error = { "GeoJSON source clusterMaxZoom value must be a number" }; + return {}; } } @@ -64,7 +69,8 @@ struct Converter<GeoJSONOptions> { if (toNumber(*clusterRadiusValue)) { options.clusterRadius = static_cast<double>(*toNumber(*clusterRadiusValue)); } else { - return Error{ "GeoJSON source clusterRadius value must be a number" }; + error = { "GeoJSON source clusterRadius value must be a number" }; + return {}; } } diff --git a/include/mbgl/style/conversion/layer.hpp b/include/mbgl/style/conversion/layer.hpp index 0539dcf9ad..efb1df8fef 100644 --- a/include/mbgl/style/conversion/layer.hpp +++ b/include/mbgl/style/conversion/layer.hpp @@ -58,47 +58,53 @@ template <> struct Converter<std::unique_ptr<Layer>> { public: template <class V> - Result<std::unique_ptr<Layer>> operator()(const V& value) const { + optional<std::unique_ptr<Layer>> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "layer must be an object" }; + error = { "layer must be an object" }; + return {}; } auto idValue = objectMember(value, "id"); if (!idValue) { - return Error { "layer must have an id" }; + error = { "layer must have an id" }; + return {}; } optional<std::string> id = toString(*idValue); if (!id) { - return Error { "layer id must be a string" }; + error = { "layer id must be a string" }; + return {}; } auto typeValue = objectMember(value, "type"); if (!typeValue) { - return Error { "layer must have a type" }; + error = { "layer must have a type" }; + return {}; } optional<std::string> type = toString(*typeValue); if (!type) { - return Error { "layer type must be a string" }; + error = { "layer type must be a string" }; + return {}; } - Result<std::unique_ptr<Layer>> converted; + optional<std::unique_ptr<Layer>> converted; if (*type == "fill") { - converted = convertVectorLayer<FillLayer>(*id, value); + converted = convertVectorLayer<FillLayer>(*id, value, error); } else if (*type == "line") { - converted = convertVectorLayer<LineLayer>(*id, value); + converted = convertVectorLayer<LineLayer>(*id, value, error); } else if (*type == "circle") { - converted = convertVectorLayer<CircleLayer>(*id, value); + converted = convertVectorLayer<CircleLayer>(*id, value, error); } else if (*type == "symbol") { - converted = convertVectorLayer<SymbolLayer>(*id, value); + converted = convertVectorLayer<SymbolLayer>(*id, value, error); } else if (*type == "raster") { - converted = convertRasterLayer(*id, value); + converted = convertRasterLayer(*id, value, error); } else if (*type == "background") { - converted = convertBackgroundLayer(*id, value); + converted = convertBackgroundLayer(*id, value, error); } else { - return Error { "invalid layer type" }; + error = { "invalid layer type" }; + return {}; } if (!converted) { @@ -111,7 +117,8 @@ public: if (minzoomValue) { optional<float> minzoom = toNumber(*minzoomValue); if (!minzoom) { - return Error { "minzoom must be numeric" }; + error = { "minzoom must be numeric" }; + return {}; } layer->setMinZoom(*minzoom); } @@ -120,7 +127,8 @@ public: if (maxzoomValue) { optional<float> maxzoom = toNumber(*maxzoomValue); if (!maxzoom) { - return Error { "maxzoom must be numeric" }; + error = { "maxzoom must be numeric" }; + return {}; } layer->setMaxZoom(*maxzoom); } @@ -128,19 +136,22 @@ public: auto layoutValue = objectMember(value, "layout"); if (layoutValue) { if (!isObject(*layoutValue)) { - return Error { "layout must be an object" }; + error = { "layout must be an object" }; + return {}; } - optional<Error> error = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { + optional<Error> error_ = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { return setLayoutProperty(*layer, k, v); }); - if (error) { - return *error; + if (error_) { + error = *error_; + return {}; } } - optional<Error> error = setPaintProperties(*layer, value); - if (error) { - return *error; + optional<Error> error_ = setPaintProperties(*layer, value); + if (error_) { + error = *error_; + return {}; } return std::move(layer); @@ -148,15 +159,17 @@ public: private: template <class LayerType, class V> - Result<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value) const { + optional<std::unique_ptr<Layer>> convertVectorLayer(const std::string& id, const V& value, Error& error) const { auto sourceValue = objectMember(value, "source"); if (!sourceValue) { - return Error { "layer must have a source" }; + error = { "layer must have a source" }; + return {}; } optional<std::string> source = toString(*sourceValue); if (!source) { - return Error { "layer source must be a string" }; + error = { "layer source must be a string" }; + return {}; } std::unique_ptr<LayerType> layer = std::make_unique<LayerType>(id, *source); @@ -165,41 +178,44 @@ private: if (sourceLayerValue) { optional<std::string> sourceLayer = toString(*sourceLayerValue); if (!sourceLayer) { - return Error { "layer source-layer must be a string" }; + error = { "layer source-layer must be a string" }; + return {}; } layer->setSourceLayer(*sourceLayer); } auto filterValue = objectMember(value, "filter"); if (filterValue) { - Result<Filter> filter = convert<Filter>(*filterValue); + optional<Filter> filter = convert<Filter>(*filterValue, error); if (!filter) { - return filter.error(); + return {}; } layer->setFilter(*filter); } - return std::move(layer); + return { std::move(layer) }; } template <class V> - Result<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value) const { + optional<std::unique_ptr<Layer>> convertRasterLayer(const std::string& id, const V& value, Error& error) const { auto sourceValue = objectMember(value, "source"); if (!sourceValue) { - return Error { "layer must have a source" }; + error = { "layer must have a source" }; + return {}; } optional<std::string> source = toString(*sourceValue); if (!source) { - return Error { "layer source must be a string" }; + error = { "layer source must be a string" }; + return {}; } - return std::make_unique<RasterLayer>(id, *source); + return { std::make_unique<RasterLayer>(id, *source) }; } template <class V> - Result<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&) const { - return std::make_unique<BackgroundLayer>(id); + optional<std::unique_ptr<Layer>> convertBackgroundLayer(const std::string& id, const V&, Error&) const { + return { std::make_unique<BackgroundLayer>(id) }; } }; diff --git a/include/mbgl/style/conversion/property_setter.hpp b/include/mbgl/style/conversion/property_setter.hpp index 51ec4778d9..1f537f3c4d 100644 --- a/include/mbgl/style/conversion/property_setter.hpp +++ b/include/mbgl/style/conversion/property_setter.hpp @@ -26,9 +26,10 @@ optional<Error> setLayoutProperty(Layer& layer, const V& value) { return Error { "layer doesn't support this property" }; } - Result<PropertyValue> typedValue = convert<PropertyValue>(value); + Error error; + optional<PropertyValue> typedValue = convert<PropertyValue>(value, error); if (!typedValue) { - return typedValue.error(); + return error; } (typedLayer->*setter)(*typedValue); @@ -42,9 +43,10 @@ optional<Error> setPaintProperty(Layer& layer, const V& value, const optional<st return Error { "layer doesn't support this property" }; } - Result<PropertyValue> typedValue = convert<PropertyValue>(value); + Error error; + optional<PropertyValue> typedValue = convert<PropertyValue>(value, error); if (!typedValue) { - return typedValue.error(); + return error; } (typedLayer->*setter)(*typedValue, klass); @@ -58,9 +60,10 @@ optional<Error> setTransition(Layer& layer, const V& value, const optional<std:: return Error { "layer doesn't support this property" }; } - Result<TransitionOptions> transition = convert<TransitionOptions>(value); + Error error; + optional<TransitionOptions> transition = convert<TransitionOptions>(value, error); if (!transition) { - return transition.error(); + return error; } (typedLayer->*setter)(*transition, klass); @@ -74,9 +77,10 @@ optional<Error> setVisibility(Layer& layer, const V& value) { return {}; } - Result<VisibilityType> visibility = convert<VisibilityType>(value); + Error error; + optional<VisibilityType> visibility = convert<VisibilityType>(value, error); if (!visibility) { - return visibility.error(); + return error; } layer.setVisibility(*visibility); diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index d5e2a5c3c8..f8937da07d 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -12,21 +12,21 @@ namespace conversion { template <class T> struct Converter<PropertyValue<T>> { template <class V> - Result<PropertyValue<T>> operator()(const V& value) const { + optional<PropertyValue<T>> operator()(const V& value, Error& error) const { if (isUndefined(value)) { - return {}; + return PropertyValue<T>(); } else if (isObject(value)) { - Result<CameraFunction<T>> function = convert<CameraFunction<T>>(value); + optional<CameraFunction<T>> function = convert<CameraFunction<T>>(value, error); if (!function) { - return function.error(); + return {}; } - return *function; + return { *function }; } else { - Result<T> constant = convert<T>(value); + optional<T> constant = convert<T>(value, error); if (!constant) { - return constant.error(); + return {}; } - return *constant; + return { *constant }; } } }; diff --git a/include/mbgl/style/conversion/source.hpp b/include/mbgl/style/conversion/source.hpp index 6e1b4347c3..2371183b1c 100644 --- a/include/mbgl/style/conversion/source.hpp +++ b/include/mbgl/style/conversion/source.hpp @@ -17,59 +17,65 @@ template <> struct Converter<std::unique_ptr<Source>> { public: template <class V> - Result<std::unique_ptr<Source>> operator()(const V& value, const std::string& id) const { + optional<std::unique_ptr<Source>> operator()(const V& value, Error& error, const std::string& id) const { if (!isObject(value)) { - return Error{ "source must be an object" }; + error = { "source must be an object" }; + return {}; } auto typeValue = objectMember(value, "type"); if (!typeValue) { - return Error{ "source must have a type" }; + error = { "source must have a type" }; + return {}; } optional<std::string> type = toString(*typeValue); if (!type) { - return Error{ "source type must be a string" }; + error = { "source type must be a string" }; + return {}; } if (*type == "raster") { - return convertRasterSource(id, value); + return convertRasterSource(id, value, error); } else if (*type == "vector") { - return convertVectorSource(id, value); + return convertVectorSource(id, value, error); } else if (*type == "geojson") { - return convertGeoJSONSource(id, value); + return convertGeoJSONSource(id, value, error); } else { - return Error{ "invalid source type" }; + error = { "invalid source type" }; + return {}; } } private: // A tile source can either specify a URL to TileJSON, or inline TileJSON. template <class V> - Result<variant<std::string, Tileset>> convertURLOrTileset(const V& value) const { + optional<variant<std::string, Tileset>> convertURLOrTileset(const V& value, Error& error) const { auto urlVal = objectMember(value, "url"); if (!urlVal) { - Result<Tileset> tileset = convert<Tileset>(value); + optional<Tileset> tileset = convert<Tileset>(value, error); if (!tileset) { - return tileset.error(); + return {}; } - return *tileset; + return { *tileset }; } optional<std::string> url = toString(*urlVal); if (!url) { - return Error{ "source url must be a string" }; + error = { "source url must be a string" }; + return {}; } - return *url; + return { *url }; } template <class V> - Result<std::unique_ptr<Source>> convertRasterSource(const std::string& id, - const V& value) const { - Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); + optional<std::unique_ptr<Source>> convertRasterSource(const std::string& id, + const V& value, + Error& error) const { + optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { - return urlOrTileset.error(); + return {}; } uint16_t tileSize = util::tileSize; @@ -77,53 +83,58 @@ private: if (tileSizeValue) { optional<float> size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits<uint16_t>::max()) { - return Error{ "invalid tileSize" }; + error = { "invalid tileSize" }; + return {}; } tileSize = *size; } - return std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize); + return { std::make_unique<RasterSource>(id, std::move(*urlOrTileset), tileSize) }; } template <class V> - Result<std::unique_ptr<Source>> convertVectorSource(const std::string& id, - const V& value) const { - Result<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value); + optional<std::unique_ptr<Source>> convertVectorSource(const std::string& id, + const V& value, + Error& error) const { + optional<variant<std::string, Tileset>> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { - return urlOrTileset.error(); + return {}; } - return std::make_unique<VectorSource>(id, std::move(*urlOrTileset)); + return { std::make_unique<VectorSource>(id, std::move(*urlOrTileset)) }; } template <class V> - Result<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, - const V& value) const { + optional<std::unique_ptr<Source>> convertGeoJSONSource(const std::string& id, + const V& value, + Error& error) const { auto dataValue = objectMember(value, "data"); if (!dataValue) { - return Error{ "GeoJSON source must have a data value" }; + error = { "GeoJSON source must have a data value" }; + return {}; } - Result<GeoJSONOptions> options = convert<GeoJSONOptions>(value); + optional<GeoJSONOptions> options = convert<GeoJSONOptions>(value, error); if (!options) { - return options.error(); + return {}; } auto result = std::make_unique<GeoJSONSource>(id, *options); if (isObject(*dataValue)) { - Result<GeoJSON> geoJSON = convertGeoJSON(*dataValue); + optional<GeoJSON> geoJSON = convertGeoJSON(*dataValue, error); if (!geoJSON) { - return geoJSON.error(); + return {}; } result->setGeoJSON(std::move(*geoJSON)); } else if (toString(*dataValue)) { result->setURL(*toString(*dataValue)); } else { - return Error{ "GeoJSON data must be a URL or an object" }; + error = { "GeoJSON data must be a URL or an object" }; + return {}; } - return std::move(result); + return { std::move(result) }; } }; diff --git a/include/mbgl/style/conversion/tileset.hpp b/include/mbgl/style/conversion/tileset.hpp index 1955cc16cf..377170aa6a 100644 --- a/include/mbgl/style/conversion/tileset.hpp +++ b/include/mbgl/style/conversion/tileset.hpp @@ -11,22 +11,25 @@ template <> struct Converter<Tileset> { public: template <class V> - Result<Tileset> operator()(const V& value) const { + optional<Tileset> operator()(const V& value, Error& error) const { Tileset result; auto tiles = objectMember(value, "tiles"); if (!tiles) { - return Error { "source must have tiles" }; + error = { "source must have tiles" }; + return {}; } if (!isArray(*tiles)) { - return Error { "source tiles must be an array" }; + error = { "source tiles must be an array" }; + return {}; } for (std::size_t i = 0; i < arrayLength(*tiles); i++) { optional<std::string> urlTemplate = toString(arrayMember(*tiles, i)); if (!urlTemplate) { - return Error { "source tiles member must be a string" }; + error = { "source tiles member must be a string" }; + return {}; } result.tiles.push_back(std::move(*urlTemplate)); } @@ -43,7 +46,8 @@ public: if (minzoomValue) { optional<float> minzoom = toNumber(*minzoomValue); if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits<uint8_t>::max()) { - return Error { "invalid minzoom" }; + error = { "invalid minzoom" }; + return {}; } result.zoomRange.min = *minzoom; } @@ -52,7 +56,8 @@ public: if (maxzoomValue) { optional<float> maxzoom = toNumber(*maxzoomValue); if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits<uint8_t>::max()) { - return Error { "invalid maxzoom" }; + error = { "invalid maxzoom" }; + return {}; } result.zoomRange.max = *maxzoom; } @@ -61,7 +66,8 @@ public: if (attributionValue) { optional<std::string> attribution = toString(*attributionValue); if (!attribution) { - return Error { "source attribution must be a string" }; + error = { "source attribution must be a string" }; + return {}; } result.attribution = std::move(*attribution); } diff --git a/include/mbgl/style/conversion/transition_options.hpp b/include/mbgl/style/conversion/transition_options.hpp index cdd65cfe9f..de8834d578 100644 --- a/include/mbgl/style/conversion/transition_options.hpp +++ b/include/mbgl/style/conversion/transition_options.hpp @@ -11,9 +11,10 @@ template <> struct Converter<TransitionOptions> { public: template <class V> - Result<TransitionOptions> operator()(const V& value) const { + optional<TransitionOptions> operator()(const V& value, Error& error) const { if (!isObject(value)) { - return Error { "transition must be an object" }; + error = { "transition must be an object" }; + return {}; } TransitionOptions result; @@ -22,7 +23,8 @@ public: if (duration) { auto number = toNumber(*duration); if (!number) { - return Error { "duration must be a number" }; + error = { "duration must be a number" }; + return {}; } result.duration = { std::chrono::milliseconds(int64_t(*number)) }; } @@ -31,7 +33,8 @@ public: if (delay) { auto number = toNumber(*delay); if (!number) { - return Error { "delay must be a number" }; + error = { "delay must be a number" }; + return {}; } result.delay = { std::chrono::milliseconds(int64_t(*number)) }; } diff --git a/platform/android/src/style/conversion/filter.hpp b/platform/android/src/style/conversion/filter.hpp index fc36d3a044..1f0abcf3a4 100644 --- a/platform/android/src/style/conversion/filter.hpp +++ b/platform/android/src/style/conversion/filter.hpp @@ -17,9 +17,10 @@ inline optional<mbgl::style::Filter> toFilter(jni::JNIEnv& env, jni::Array<jni:: mbgl::optional<mbgl::style::Filter> filter; if (jfilter) { Value filterValue(env, jfilter); - auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(filterValue); + mbgl::style::conversion::Error error; + auto converted = mbgl::style::conversion::convert<mbgl::style::Filter>(filterValue, error); if (!converted) { - mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + converted.error().message); + mbgl::Log::Error(mbgl::Event::JNI, "Error converting filter: " + error.message); } filter = std::move(*converted); } diff --git a/platform/android/src/style/conversion/geojson.hpp b/platform/android/src/style/conversion/geojson.hpp index 415d96f467..6ac6abcd7c 100644 --- a/platform/android/src/style/conversion/geojson.hpp +++ b/platform/android/src/style/conversion/geojson.hpp @@ -17,12 +17,13 @@ namespace style { namespace conversion { template <> -Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) { +optional<GeoJSON> convertGeoJSON(const mbgl::android::Value& value, Error& error) { // Value should be a string wrapped in an object mbgl::android::Value jsonValue = value.get("data"); if(value.isNull()) { - return Error { "no json data found" }; + error = { "no json data found" }; + return {}; } std::string jsonString = value.get("data").toString(); @@ -32,12 +33,14 @@ Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) { if (d.HasParseError()) { std::stringstream message; message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); - return Error { message.str() }; + error = { message.str() }; + return {}; } - conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d); + optional<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d, error); if (!geoJSON) { - return Error { geoJSON.error().message }; + error = { error.message }; + return {}; } return geoJSON; @@ -46,8 +49,8 @@ Result<GeoJSON> convertGeoJSON(const mbgl::android::Value& value) { template <> struct Converter<GeoJSON> { - Result<GeoJSON> operator()(const mbgl::android::Value& value) const { - return convertGeoJSON(value); + optional<GeoJSON> operator()(const mbgl::android::Value& value, Error& error) const { + return convertGeoJSON(value, error); } }; diff --git a/platform/android/src/style/conversion/url_or_tileset.hpp b/platform/android/src/style/conversion/url_or_tileset.hpp index 4e502324d0..00ef913d41 100644 --- a/platform/android/src/style/conversion/url_or_tileset.hpp +++ b/platform/android/src/style/conversion/url_or_tileset.hpp @@ -12,27 +12,25 @@ #include <string> namespace mbgl { -namespace style { -namespace conversion { - -template <> -struct Converter<variant<std::string, Tileset>> { - - template <class V> - Result<variant<std::string, Tileset>> operator()(const V& value) const { - if (isObject(value)) { - Result<Tileset> tileset = convert<Tileset>(value); - if (!tileset) { - return tileset.error(); - } - return *tileset; - } else { - return *toString(value); +namespace android { + +// This conversion is expected not to fail because it's used only in contexts where +// the value was originally a String or TileSet object on the Java side. If it fails +// to convert, it's a bug in our serialization or Java-side static typing. +inline variant<std::string, Tileset> convertURLOrTileset(const Value& value) { + using namespace mbgl::style::conversion; + + if (isObject(value)) { + Error error; + optional<Tileset> tileset = convert<Tileset>(value, error); + if (!tileset) { + throw std::logic_error(error.message); } + return { *tileset }; + } else { + return { *toString(value) }; } - -}; - } + } } diff --git a/platform/android/src/style/layers/layer.cpp b/platform/android/src/style/layers/layer.cpp index dbf71fd2af..6e93cf4daf 100644 --- a/platform/android/src/style/layers/layer.cpp +++ b/platform/android/src/style/layers/layer.cpp @@ -104,9 +104,10 @@ namespace android { Value wrapped(env, jfilter); Filter filter; - Result<Filter> converted = convert<Filter>(wrapped); + Error error; + optional<Filter> converted = convert<Filter>(wrapped, error); if (!converted) { - mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + converted.error().message); + mbgl::Log::Error(mbgl::Event::JNI, "Error setting filter: " + error.message); return; } filter = std::move(*converted); diff --git a/platform/android/src/style/sources/geojson_source.cpp b/platform/android/src/style/sources/geojson_source.cpp index 7ab98e47c0..0c2d25f9fc 100644 --- a/platform/android/src/style/sources/geojson_source.cpp +++ b/platform/android/src/style/sources/geojson_source.cpp @@ -9,7 +9,7 @@ #include "../../conversion/conversion.hpp" #include "../../conversion/collection.hpp" #include "../../geometry/conversion/feature.hpp" -#include "../conversion/url_or_tileset.hpp" + #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/geojson_options.hpp> @@ -18,11 +18,26 @@ namespace mbgl { namespace android { + // This conversion is expected not to fail because it's used only in contexts where + // the value was originally a GeoJsonOptions object on the Java side. If it fails + // to convert, it's a bug in our serialization or Java-side static typing. + static style::GeoJSONOptions convertGeoJSONOptions(jni::JNIEnv& env, jni::Object<> options) { + using namespace mbgl::style::conversion; + if (!options) { + return style::GeoJSONOptions(); + } + Error error; + optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(Value(env, options), error); + if (!result) { + throw std::logic_error(error.message); + } + return *result; + } + GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, jni::String sourceId, jni::Object<> options) : Source(env, std::make_unique<mbgl::style::GeoJSONSource>( jni::Make<std::string>(env, sourceId), - options ? *style::conversion::convert<style::GeoJSONOptions>(Value(env, options)) : style::GeoJSONOptions() - ) + convertGeoJSONOptions(env, options)) ) { } @@ -36,9 +51,10 @@ namespace android { using namespace mbgl::style::conversion; // Convert the jni object - Result<GeoJSON> converted = convert<GeoJSON>(Value(env, json)); + Error error; + optional<GeoJSON> converted = convert<GeoJSON>(Value(env, json), error); if(!converted) { - mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + converted.error().message); + mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message); return; } diff --git a/platform/android/src/style/sources/raster_source.cpp b/platform/android/src/style/sources/raster_source.cpp index 42ac4cda99..0234901a77 100644 --- a/platform/android/src/style/sources/raster_source.cpp +++ b/platform/android/src/style/sources/raster_source.cpp @@ -16,7 +16,7 @@ namespace android { env, std::make_unique<mbgl::style::RasterSource>( jni::Make<std::string>(env, sourceId), - *style::conversion::convert<variant<std::string, Tileset>>(Value(env, urlOrTileSet)), + convertURLOrTileset(Value(env, urlOrTileSet)), tileSize ) ) { diff --git a/platform/android/src/style/sources/vector_source.cpp b/platform/android/src/style/sources/vector_source.cpp index 67777f50d4..4852a9b84f 100644 --- a/platform/android/src/style/sources/vector_source.cpp +++ b/platform/android/src/style/sources/vector_source.cpp @@ -23,7 +23,7 @@ namespace android { env, std::make_unique<mbgl::style::VectorSource>( jni::Make<std::string>(env, sourceId), - *style::conversion::convert<variant<std::string, Tileset>>(Value(env, urlOrTileSet)) + convertURLOrTileset(Value(env, urlOrTileSet)) ) ) { } diff --git a/platform/darwin/src/MGLStyleValue_Private.h b/platform/darwin/src/MGLStyleValue_Private.h index 90d6622c93..263b54d7e5 100644 --- a/platform/darwin/src/MGLStyleValue_Private.h +++ b/platform/darwin/src/MGLStyleValue_Private.h @@ -123,8 +123,9 @@ public: return toMBGLConstantValue((MGLConstantStyleValue<ObjCType> *)value); } else if ([value isKindOfClass:[MGLStyleFunction class]]) { auto rawValue = toRawStyleSpecValue((MGLStyleFunction<ObjCType> *) value); - auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>(rawValue); - NSCAssert(result, @(result.error().message.c_str())); + mbgl::style::conversion::Error error; + auto result = mbgl::style::conversion::convert<mbgl::style::DataDrivenPropertyValue<MBGLType>>(rawValue, error); + NSCAssert(result, @(error.message.c_str())); return *result; } else { return {}; diff --git a/platform/node/src/node_geojson.hpp b/platform/node/src/node_geojson.hpp index ace4c91426..e4b2ca47e7 100644 --- a/platform/node/src/node_geojson.hpp +++ b/platform/node/src/node_geojson.hpp @@ -5,8 +5,9 @@ namespace style { namespace conversion { template <> -Result<GeoJSON> convertGeoJSON(const v8::Local<v8::Value>&) { - return Error { "not implemented" }; +optional<GeoJSON> convertGeoJSON(const v8::Local<v8::Value>&, Error& error) { + error = { "not implemented" }; + return {}; } } // namespace conversion diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index a3a11a1907..881c1b2fb7 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -538,9 +538,10 @@ void NodeMap::AddSource(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("First argument must be a string"); } - Result<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], *Nan::Utf8String(info[0])); + Error error; + mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(info[1], error, *Nan::Utf8String(info[0])); if (!source) { - Nan::ThrowTypeError(source.error().message.c_str()); + Nan::ThrowTypeError(error.message.c_str()); return; } @@ -558,9 +559,10 @@ void NodeMap::AddLayer(const Nan::FunctionCallbackInfo<v8::Value>& info) { return Nan::ThrowTypeError("One argument required"); } - Result<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(info[0]); + Error error; + mbgl::optional<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(info[0], error); if (!layer) { - Nan::ThrowTypeError(layer.error().message.c_str()); + Nan::ThrowTypeError(error.message.c_str()); return; } @@ -774,9 +776,10 @@ void NodeMap::SetFilter(const Nan::FunctionCallbackInfo<v8::Value>& info) { Filter filter; if (!info[1]->IsNull() && !info[1]->IsUndefined()) { - Result<Filter> converted = convert<Filter>(info[1]); + Error error; + mbgl::optional<Filter> converted = convert<Filter>(info[1], error); if (!converted) { - Nan::ThrowTypeError(converted.error().message.c_str()); + Nan::ThrowTypeError(error.message.c_str()); return; } filter = std::move(*converted); @@ -908,23 +911,24 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& //Check if filter is provided. If set it must be a valid Filter object if (Nan::Has(options, Nan::New("filter").ToLocalChecked()).FromJust()) { auto filterOption = Nan::Get(options, Nan::New("filter").ToLocalChecked()).ToLocalChecked(); - Result<Filter> converted = convert<Filter>(filterOption); + Error error; + mbgl::optional<Filter> converted = convert<Filter>(filterOption, error); if (!converted) { - return Nan::ThrowTypeError(converted.error().message.c_str()); + return Nan::ThrowTypeError(error.message.c_str()); } queryOptions.filter = std::move(*converted); } } try { - std::vector<mbgl::Feature> result; + std::vector<mbgl::Feature> optional; if (Nan::Get(posOrBox, 0).ToLocalChecked()->IsArray()) { auto pos0 = Nan::Get(posOrBox, 0).ToLocalChecked().As<v8::Array>(); auto pos1 = Nan::Get(posOrBox, 1).ToLocalChecked().As<v8::Array>(); - result = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox { + optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenBox { { Nan::Get(pos0, 0).ToLocalChecked()->NumberValue(), Nan::Get(pos0, 1).ToLocalChecked()->NumberValue() @@ -935,15 +939,15 @@ void NodeMap::QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>& }, queryOptions); } else { - result = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate { + optional = nodeMap->map->queryRenderedFeatures(mbgl::ScreenCoordinate { Nan::Get(posOrBox, 0).ToLocalChecked()->NumberValue(), Nan::Get(posOrBox, 1).ToLocalChecked()->NumberValue() }, queryOptions); } auto array = Nan::New<v8::Array>(); - for (unsigned int i = 0; i < result.size(); i++) { - array->Set(i, toJS(result[i])); + for (unsigned int i = 0; i < optional.size(); i++) { + array->Set(i, toJS(optional[i])); } info.GetReturnValue().Set(array); } catch (const std::exception &ex) { diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index b1c0a11ee1..3d5da10c36 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -1250,9 +1250,10 @@ void QMapboxGL::addSource(const QString &id, const QVariantMap ¶ms) using namespace mbgl::style; using namespace mbgl::style::conversion; - Result<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(QVariant(params), id.toStdString()); + Error error; + mbgl::optional<std::unique_ptr<Source>> source = convert<std::unique_ptr<Source>>(QVariant(params), error, id.toStdString()); if (!source) { - qWarning() << "Unable to add source:" << source.error().message.c_str(); + qWarning() << "Unable to add source:" << error.message.c_str(); return; } @@ -1291,7 +1292,8 @@ void QMapboxGL::updateSource(const QString &id, const QVariantMap ¶ms) } if (params.contains("data")) { - auto result = convertGeoJSON(params["data"]); + Error error; + auto result = convertGeoJSON(params["data"], error); if (result) { sourceGeoJSON->setGeoJSON(*result); } @@ -1362,9 +1364,10 @@ void QMapboxGL::addLayer(const QVariantMap ¶ms) using namespace mbgl::style; using namespace mbgl::style::conversion; - Result<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(QVariant(params)); + Error error; + mbgl::optional<std::unique_ptr<Layer>> layer = convert<std::unique_ptr<Layer>>(QVariant(params), error); if (!layer) { - qWarning() << "Unable to add layer:" << layer.error().message.c_str(); + qWarning() << "Unable to add layer:" << error.message.c_str(); return; } @@ -1445,9 +1448,10 @@ void QMapboxGL::setFilter(const QString& layer, const QVariant& filter) Filter filter_; - Result<Filter> converted = convert<Filter>(filter); + Error error; + mbgl::optional<Filter> converted = convert<Filter>(filter, error); if (!converted) { - qWarning() << "Error parsing filter:" << converted.error().message.c_str(); + qWarning() << "Error parsing filter:" << error.message.c_str(); return; } filter_ = std::move(*converted); diff --git a/platform/qt/src/qt_geojson.hpp b/platform/qt/src/qt_geojson.hpp index 038b051941..7c50c663dd 100644 --- a/platform/qt/src/qt_geojson.hpp +++ b/platform/qt/src/qt_geojson.hpp @@ -180,20 +180,21 @@ namespace style { namespace conversion { template <> -Result<GeoJSON> convertGeoJSON(const QMapbox::Feature& feature) { - return Result<GeoJSON> { GeoJSON { asMapboxGLFeature(feature) } }; +optional<GeoJSON> convertGeoJSON(const QMapbox::Feature& feature, Error&) { + return GeoJSON { asMapboxGLFeature(feature) }; } template <> -Result<GeoJSON> convertGeoJSON(const QVariant& value) { +optional<GeoJSON> convertGeoJSON(const QVariant& value, Error& error) { #if QT_VERSION >= 0x050000 if (value.typeName() == QStringLiteral("QMapbox::Feature")) { #else if (value.typeName() == QString("QMapbox::Feature")) { #endif - return convertGeoJSON(value.value<QMapbox::Feature>()); + return convertGeoJSON(value.value<QMapbox::Feature>(), error); } else if (value.type() != QVariant::ByteArray) { - return Error { "JSON data must be in QByteArray" }; + error = { "JSON data must be in QByteArray" }; + return {}; } auto data = value.toByteArray(); @@ -208,13 +209,14 @@ Result<GeoJSON> convertGeoJSON(const QVariant& value) { if (d.HasParseError()) { std::stringstream message; message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); - - return Error { message.str() }; + error = { message.str() }; + return {}; } - Result<GeoJSON> geoJSON = convertGeoJSON<JSValue>(d); + optional<GeoJSON> geoJSON = convertGeoJSON<JSValue>(d, error); if (!geoJSON) { - return Error { geoJSON.error().message }; + error = { error.message }; + return {}; } return geoJSON; diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp index 926c243733..d970d8bbc0 100644 --- a/src/mbgl/style/parser.cpp +++ b/src/mbgl/style/parser.cpp @@ -116,10 +116,11 @@ void Parser::parseSources(const JSValue& value) { for (const auto& property : value.GetObject()) { std::string id = *conversion::toString(property.name); - conversion::Result<std::unique_ptr<Source>> source = - conversion::convert<std::unique_ptr<Source>>(property.value, id); + conversion::Error error; + optional<std::unique_ptr<Source>> source = + conversion::convert<std::unique_ptr<Source>>(property.value, error, id); if (!source) { - Log::Warning(Event::ParseStyle, source.error().message); + Log::Warning(Event::ParseStyle, error.message); continue; } @@ -222,9 +223,10 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique layer = reference->baseImpl->cloneRef(id); conversion::setPaintProperties(*layer, value); } else { - conversion::Result<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value); + conversion::Error error; + optional<std::unique_ptr<Layer>> converted = conversion::convert<std::unique_ptr<Layer>>(value, error); if (!converted) { - Log::Warning(Event::ParseStyle, converted.error().message); + Log::Warning(Event::ParseStyle, error.message); return; } layer = std::move(*converted); @@ -232,20 +234,20 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique } std::vector<FontStack> Parser::fontStacks() const { - std::set<FontStack> result; + std::set<FontStack> optional; for (const auto& layer : layers) { if (layer->is<SymbolLayer>()) { PropertyValue<FontStack> textFont = layer->as<SymbolLayer>()->getTextFont(); if (textFont.isUndefined()) { - result.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); + optional.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); } else if (textFont.isConstant()) { - result.insert(textFont.asConstant()); + optional.insert(textFont.asConstant()); } else if (textFont.isCameraFunction()) { textFont.asCameraFunction().stops.match( [&] (const auto& stops) { for (const auto& stop : stops.stops) { - result.insert(stop.second); + optional.insert(stop.second); } } ); @@ -253,7 +255,7 @@ std::vector<FontStack> Parser::fontStacks() const { } } - return std::vector<FontStack>(result.begin(), result.end()); + return std::vector<FontStack>(optional.begin(), optional.end()); } } // namespace style diff --git a/src/mbgl/style/property_parsing.cpp b/src/mbgl/style/property_parsing.cpp deleted file mode 100644 index 16ce0f4adc..0000000000 --- a/src/mbgl/style/property_parsing.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include <mbgl/style/property_parsing.hpp> - -namespace mbgl { -namespace style { - -optional<TransitionOptions> parseTransitionOptions(const char *, const JSValue& value) { - if (!value.IsObject()) { - return {}; - } - - optional<Duration> duration; - if (value.HasMember("duration") && value["duration"].IsNumber()) { - duration.emplace(Milliseconds(value["duration"].GetUint())); - } - - optional<Duration> delay; - if (value.HasMember("delay") && value["delay"].IsNumber()) { - delay.emplace(Milliseconds(value["delay"].GetUint())); - } - - if (!duration && !delay) { - return {}; - } - - return TransitionOptions { duration, delay }; -} - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/property_parsing.hpp b/src/mbgl/style/property_parsing.hpp deleted file mode 100644 index b542c8ae47..0000000000 --- a/src/mbgl/style/property_parsing.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include <mbgl/style/property_value.hpp> -#include <mbgl/style/transition_options.hpp> -#include <mbgl/style/rapidjson_conversion.hpp> -#include <mbgl/style/conversion/property_value.hpp> - -#include <mbgl/util/logging.hpp> - -namespace mbgl { -namespace style { - -template <typename T> -PropertyValue<T> parseProperty(const char* name, const JSValue& value) { - conversion::Result<PropertyValue<T>> result = conversion::convert<PropertyValue<T>>(value); - if (!result) { - Log::Warning(Event::ParseStyle, "%s: %s", name, result.error().message); - return {}; - } - return *result; -} - -optional<TransitionOptions> parseTransitionOptions(const char * name, const JSValue&); - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/sources/geojson_source_impl.cpp b/src/mbgl/style/sources/geojson_source_impl.cpp index b050e5482e..6431d5faa4 100644 --- a/src/mbgl/style/sources/geojson_source_impl.cpp +++ b/src/mbgl/style/sources/geojson_source_impl.cpp @@ -21,11 +21,12 @@ namespace style { namespace conversion { template <> -Result<GeoJSON> convertGeoJSON(const JSValue& value) { +optional<GeoJSON> convertGeoJSON(const JSValue& value, Error& error) { try { return mapbox::geojson::convert(value); } catch (const std::exception& ex) { - return Error{ ex.what() }; + error = { ex.what() }; + return {}; } } } // namespace conversion @@ -136,10 +137,11 @@ void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { invalidateTiles(); - conversion::Result<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d); + conversion::Error error; + optional<GeoJSON> geoJSON = conversion::convertGeoJSON<JSValue>(d, error); if (!geoJSON) { Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", - geoJSON.error().message.c_str()); + error.message.c_str()); // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for // tiles to load. _setGeoJSON(GeoJSON{ FeatureCollection{} }); diff --git a/src/mbgl/style/tile_source_impl.cpp b/src/mbgl/style/tile_source_impl.cpp index daa6389a8e..9ea596d38b 100644 --- a/src/mbgl/style/tile_source_impl.cpp +++ b/src/mbgl/style/tile_source_impl.cpp @@ -24,9 +24,10 @@ Tileset TileSourceImpl::parseTileJSON(const std::string& json, const std::string throw std::runtime_error(message.str()); } - conversion::Result<Tileset> result = conversion::convert<Tileset, JSValue>(document); + conversion::Error error; + optional<Tileset> result = conversion::convert<Tileset, JSValue>(document, error); if (!result) { - throw std::runtime_error(result.error().message); + throw std::runtime_error(error.message); } // TODO: Remove this hack by delivering proper URLs in the TileJSON to begin with. diff --git a/test/style/conversion/function.test.cpp b/test/style/conversion/function.test.cpp index 4dc6549c78..08637d40cb 100644 --- a/test/style/conversion/function.test.cpp +++ b/test/style/conversion/function.test.cpp @@ -10,45 +10,47 @@ using namespace mbgl; using namespace mbgl::style; using namespace mbgl::style::conversion; -auto parseFunction(const std::string& src) { - JSDocument doc; - doc.Parse<0>(src); - return convert<CameraFunction<float>, JSValue>(doc); -} - TEST(StyleConversion, Function) { + Error error; + + auto parseFunction = [&](const std::string& src) { + JSDocument doc; + doc.Parse<0>(src); + return convert<CameraFunction<float>, JSValue>(doc, error); + }; + auto fn1 = parseFunction("{\"stops\":[]}"); ASSERT_FALSE(fn1); - ASSERT_EQ("function must have at least one stop", fn1.error().message); + ASSERT_EQ("function must have at least one stop", error.message); auto fn2 = parseFunction("{\"stops\":[1]}"); ASSERT_FALSE(fn2); - ASSERT_EQ("function stop must be an array", fn2.error().message); + ASSERT_EQ("function stop must be an array", error.message); auto fn3 = parseFunction("{\"stops\":[[]]}"); ASSERT_FALSE(fn3); - ASSERT_EQ("function stop must have two elements", fn3.error().message); + ASSERT_EQ("function stop must have two elements", error.message); auto fn4 = parseFunction("{\"stops\":[[-1,-1]]}"); ASSERT_TRUE(bool(fn4)); auto fn5 = parseFunction("{\"stops\":[[0,1,2]]}"); ASSERT_FALSE(fn5); - ASSERT_EQ("function stop must have two elements", fn5.error().message); + ASSERT_EQ("function stop must have two elements", error.message); auto fn6 = parseFunction("{\"stops\":[[0,\"x\"]]}"); ASSERT_FALSE(fn6); - ASSERT_EQ("value must be a number", fn6.error().message); + ASSERT_EQ("value must be a number", error.message); auto fn7 = parseFunction("{}"); ASSERT_FALSE(fn7); - ASSERT_EQ("function value must specify stops", fn7.error().message); + ASSERT_EQ("function value must specify stops", error.message); auto fn8 = parseFunction("[]"); ASSERT_FALSE(fn8); - ASSERT_EQ("function must be an object", fn8.error().message); + ASSERT_EQ("function must be an object", error.message); auto fn9 = parseFunction("{\"stops\":[[0,0]],\"base\":false}"); ASSERT_FALSE(fn9); - ASSERT_EQ("function base must be a number", fn9.error().message); + ASSERT_EQ("function base must be a number", error.message); } diff --git a/test/style/conversion/geojson_options.test.cpp b/test/style/conversion/geojson_options.test.cpp index ddf261ea52..e6bd984f36 100644 --- a/test/style/conversion/geojson_options.test.cpp +++ b/test/style/conversion/geojson_options.test.cpp @@ -12,21 +12,24 @@ using namespace mbgl::style::conversion; TEST(GeoJSONOptions, Basic) { ValueMap map; Value raw(map); - Result<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw); + Error error; + mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error); ASSERT_TRUE((bool) converted); } TEST(GeoJSONOptions, ErrorHandling) { ValueMap map {{"maxzoom", std::string{"should not be a string"}}}; Value raw(map); - Result<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw); + Error error; + mbgl::optional<GeoJSONOptions> converted = convert<GeoJSONOptions>(raw, error); ASSERT_FALSE((bool) converted); } TEST(GeoJSONOptions, RetainsDefaults) { ValueMap map; Value raw(map); - GeoJSONOptions converted = *convert<GeoJSONOptions>(raw); + Error error; + GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error); GeoJSONOptions defaults; // GeoJSON-VT @@ -54,7 +57,8 @@ TEST(GeoJSONOptions, FullConversion) { {"clusterMaxZoom", 5.0f} }; Value raw(map); - GeoJSONOptions converted = *convert<GeoJSONOptions>(raw); + Error error; + GeoJSONOptions converted = *convert<GeoJSONOptions>(raw, error); // GeoJSON-VT ASSERT_EQ(converted.maxzoom, 1); diff --git a/test/style/conversion/layer.test.cpp b/test/style/conversion/layer.test.cpp index b27c1841ee..cfe2662fb8 100644 --- a/test/style/conversion/layer.test.cpp +++ b/test/style/conversion/layer.test.cpp @@ -11,10 +11,11 @@ using namespace mbgl::style; using namespace mbgl::style::conversion; using namespace std::literals::chrono_literals; -auto parseLayer(const std::string& src) { +std::unique_ptr<Layer> parseLayer(const std::string& src) { JSDocument doc; doc.Parse<0>(src); - return convert<std::unique_ptr<Layer>, JSValue>(doc); + Error error; + return std::move(*convert<std::unique_ptr<Layer>, JSValue>(doc, error)); } TEST(StyleConversion, LayerTransition) { @@ -34,13 +35,13 @@ TEST(StyleConversion, LayerTransition) { } })JSON"); - ASSERT_EQ(400ms, *(*layer)->as<BackgroundLayer>()->impl->paint.cascading + ASSERT_EQ(400ms, *layer->as<BackgroundLayer>()->impl->paint.cascading .get<BackgroundColor>().getTransition({}).duration); - ASSERT_EQ(500ms, *(*layer)->as<BackgroundLayer>()->impl->paint.cascading + ASSERT_EQ(500ms, *layer->as<BackgroundLayer>()->impl->paint.cascading .get<BackgroundColor>().getTransition({}).delay); - ASSERT_EQ(100ms, *(*layer)->as<BackgroundLayer>()->impl->paint.cascading + ASSERT_EQ(100ms, *layer->as<BackgroundLayer>()->impl->paint.cascading .get<BackgroundColor>().getTransition({"class"}).duration); - ASSERT_FALSE(bool((*layer)->as<BackgroundLayer>()->impl->paint.cascading + ASSERT_FALSE(bool(layer->as<BackgroundLayer>()->impl->paint.cascading .get<BackgroundColor>().getTransition({"class"}).delay)); } diff --git a/test/style/filter.test.cpp b/test/style/filter.test.cpp index 2f12246078..c70792d8ef 100644 --- a/test/style/filter.test.cpp +++ b/test/style/filter.test.cpp @@ -16,7 +16,10 @@ using namespace mbgl::style; Filter parse(const char * expression) { rapidjson::GenericDocument<rapidjson::UTF8<>, rapidjson::CrtAllocator> doc; doc.Parse<0>(expression); - return *conversion::convert<Filter, JSValue>(doc); + conversion::Error error; + optional<Filter> filter = conversion::convert<Filter, JSValue>(doc, error); + EXPECT_TRUE(bool(filter)); + return *filter; } Feature feature(const PropertyMap& properties, const Geometry<double>& geometry = Point<double>()) { |