From d7227e13a7a87cf50a4c8c1f0615fc565f5a2679 Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Tue, 7 Mar 2017 17:00:53 -0800 Subject: [all] Replace Result with optional plus out Error parameter --- include/mbgl/style/conversion.hpp | 39 +---- include/mbgl/style/conversion/constant.hpp | 63 +++++--- .../conversion/data_driven_property_value.hpp | 18 +-- include/mbgl/style/conversion/filter.hpp | 157 +++++++++--------- include/mbgl/style/conversion/function.hpp | 175 ++++++++++++--------- include/mbgl/style/conversion/geojson.hpp | 2 +- include/mbgl/style/conversion/geojson_options.hpp | 20 ++- include/mbgl/style/conversion/layer.hpp | 88 ++++++----- include/mbgl/style/conversion/property_setter.hpp | 20 ++- include/mbgl/style/conversion/property_value.hpp | 16 +- include/mbgl/style/conversion/source.hpp | 79 ++++++---- include/mbgl/style/conversion/tileset.hpp | 20 ++- .../mbgl/style/conversion/transition_options.hpp | 11 +- 13 files changed, 391 insertions(+), 317 deletions(-) (limited to 'include') 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 +#include #include @@ -21,11 +21,11 @@ namespace conversion { A single template function serves as the public interface: template - Result convert(const V& value); + optional 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 Result : private variant { -public: - using variant::variant; - - explicit operator bool() const { - return this->template is(); - } - - T& operator*() { - assert(this->template is()); - return this->template get(); - } - - const T& operator*() const { - assert(this->template is()); - return this->template get(); - } - - const Error& error() const { - assert(this->template is()); - return this->template get(); - } -}; - template struct Converter; template -Result convert(const V& value, Args&&...args) { - return Converter()(value, std::forward(args)...); +optional convert(const V& value, Error& error, Args&&...args) { + return Converter()(value, error, std::forward(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 { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { optional 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 { template <> struct Converter { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { optional 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 { template <> struct Converter { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { optional 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 { template struct Converter::value>> { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { optional string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } const auto result = Enum::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::value>> { template <> struct Converter { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { optional string = toString(value); if (!string) { - return Error { "value must be a string" }; + error = { "value must be a string" }; + return {}; } optional 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 { template <> struct Converter> { template - Result> operator()(const V& value) const { + optional> 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 first = toNumber(arrayMember(value, 0)); optional 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 {{ *first, *second }}; @@ -106,9 +115,10 @@ struct Converter> { template <> struct Converter> { template - Result> operator()(const V& value) const { + optional> 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 first = toNumber(arrayMember(value, 0)); @@ -116,7 +126,8 @@ struct Converter> { optional third = toNumber(arrayMember(value, 2)); optional 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 {{ *first, *second, *third, *fourth }}; @@ -126,9 +137,10 @@ struct Converter> { template <> struct Converter> { template - Result> operator()(const V& value) const { + optional> 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 result; @@ -137,7 +149,8 @@ struct Converter> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional 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> { template <> struct Converter> { template - Result> operator()(const V& value) const { + optional> 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 result; @@ -160,7 +174,8 @@ struct Converter> { for (std::size_t i = 0; i < arrayLength(value); ++i) { optional 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 struct Converter> { template - Result> operator()(const V& value) const { + optional> operator()(const V& value, Error& error) const { if (isUndefined(value)) { - return {}; + return DataDrivenPropertyValue(); } else if (!isObject(value)) { - Result constant = convert(value); + optional constant = convert(value, error); if (!constant) { - return constant.error(); + return {}; } return DataDrivenPropertyValue(*constant); } else if (!objectMember(value, "property")) { - Result> function = convert>(value); + optional> function = convert>(value, error); if (!function) { - return function.error(); + return {}; } return DataDrivenPropertyValue(*function); } else { - Result> composite = convert>(value); + optional> composite = convert>(value, error); if (composite) { return DataDrivenPropertyValue(*composite); } - Result> source = convert>(value); + optional> source = convert>(value, error); if (!source) { - return source.error(); + return {}; } return DataDrivenPropertyValue(*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 { public: template - Result operator()(const V& value) const { + optional 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 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(value); + return convertEqualityFilter(value, error); } else if (*op == "!=") { - return convertEqualityFilter(value); + return convertEqualityFilter(value, error); } else if (*op == ">") { - return convertBinaryFilter(value); + return convertBinaryFilter(value, error); } else if (*op == ">=") { - return convertBinaryFilter(value); + return convertBinaryFilter(value, error); } else if (*op == "<") { - return convertBinaryFilter(value); + return convertBinaryFilter(value, error); } else if (*op == "<=") { - return convertBinaryFilter(value); + return convertBinaryFilter(value, error); } else if (*op == "in") { - return convertSetFilter(value); + return convertSetFilter(value, error); } else if (*op == "!in") { - return convertSetFilter(value); + return convertSetFilter(value, error); } else if (*op == "all") { - return convertCompoundFilter(value); + return convertCompoundFilter(value, error); } else if (*op == "any") { - return convertCompoundFilter(value); + return convertCompoundFilter(value, error); } else if (*op == "none") { - return convertCompoundFilter(value); + return convertCompoundFilter(value, error); } else if (*op == "has") { - return convertUnaryFilter(value); + return convertUnaryFilter(value, error); } else if (*op == "!has") { - return convertUnaryFilter(value); + return convertUnaryFilter(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 normalizeValue(const optional& value) const { + optional normalizeValue(const optional& 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 - Result toFeatureType(const V& value) const { + optional toFeatureType(const V& value, Error& error) const { optional 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 - Result toFeatureIdentifier(const V& value) const { + optional toFeatureIdentifier(const V& value, Error& error) const { optional 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 { return t; }, - [] ( int64_t t) -> Result { return t; }, - [] ( double t) -> Result { return t; }, - [] (const std::string& t) -> Result { return t; }, - [] (const auto&) -> Result { - return Error { "filter expression value must be a boolean, number, or string" }; + [] (uint64_t t) -> optional { return { t }; }, + [] ( int64_t t) -> optional { return { t }; }, + [] ( double t) -> optional { return { t }; }, + [] (const std::string& t) -> optional { return { t }; }, + [&] (const auto&) -> optional { + error = { "filter expression value must be a boolean, number, or string" }; + return {}; }); } } template - Result convertUnaryFilter(const V& value) const { + optional 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 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 - Result convertEqualityFilter(const V& value) const { + optional 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 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 filterValue = toFeatureType(arrayMember(value, 2)); + optional filterValue = toFeatureType(arrayMember(value, 2), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return TypeFilterType { *filterValue }; + return { TypeFilterType { *filterValue } }; } else if (*key == "$id") { - Result filterValue = toFeatureIdentifier(arrayMember(value, 2)); + optional filterValue = toFeatureIdentifier(arrayMember(value, 2), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return IdentifierFilterType { *filterValue }; + return { IdentifierFilterType { *filterValue } }; } else { - Result filterValue = normalizeValue(toValue(arrayMember(value, 2))); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return FilterType { *key, *filterValue }; + return { FilterType { *key, *filterValue } }; } } template - Result convertBinaryFilter(const V& value) const { + optional 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 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 filterValue = normalizeValue(toValue(arrayMember(value, 2))); + optional filterValue = normalizeValue(toValue(arrayMember(value, 2)), error); if (!filterValue) { - return filterValue.error(); + return {}; } - return FilterType { *key, *filterValue }; + return { FilterType { *key, *filterValue } }; } template - Result convertSetFilter(const V& value) const { + optional 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 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 values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result filterValue = toFeatureType(arrayMember(value, i)); + optional 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 values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result filterValue = toFeatureIdentifier(arrayMember(value, i)); + optional 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 values; for (std::size_t i = 2; i < arrayLength(value); ++i) { - Result filterValue = normalizeValue(toValue(arrayMember(value, i))); + optional 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 - Result convertCompoundFilter(const V& value) const { + optional convertCompoundFilter(const V& value, Error& error) const { std::vector filters; for (std::size_t i = 1; i < arrayLength(value); ++i) { - Result element = operator()(arrayMember(value, i)); + optional 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 -Result> convertStops(const V& value) { +optional> 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 stops; @@ -31,21 +34,23 @@ Result> 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 = convert(arrayMember(stopValue, 0)); + optional d = convert(arrayMember(stopValue, 0), error); if (!d) { - return d.error(); + return {}; } - Result r = convert(arrayMember(stopValue, 1)); + optional r = convert(arrayMember(stopValue, 1), error); if (!r) { - return r.error(); + return {}; } stops.emplace(*d, *r); @@ -59,10 +64,10 @@ struct Converter> { static constexpr const char * type = "exponential"; template - Result> operator()(const V& value) const { - auto stops = convertStops(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops(value, error); if (!stops) { - return stops.error(); + return {}; } auto baseValue = objectMember(value, "base"); @@ -72,7 +77,8 @@ struct Converter> { optional base = toNumber(*baseValue); if (!base) { - return Error { "function base must be a number"}; + error = { "function base must be a number"}; + return {}; } return ExponentialStops(*stops, *base); @@ -84,10 +90,10 @@ struct Converter> { static constexpr const char * type = "interval"; template - Result> operator()(const V& value) const { - auto stops = convertStops(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops(value, error); if (!stops) { - return stops.error(); + return {}; } return IntervalStops(*stops); } @@ -96,23 +102,24 @@ struct Converter> { template <> struct Converter { template - Result operator()(const V& value) const { + optional 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> { static constexpr const char * type = "categorical"; template - Result> operator()(const V& value) const { - auto stops = convertStops(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops(value, error); if (!stops) { - return stops.error(); + return {}; } return CategoricalStops( std::map((*stops).begin(), (*stops).end())); @@ -136,7 +143,7 @@ struct Converter> { static constexpr const char * type = "identity"; template - Result> operator()(const V&) const { + optional> operator()(const V&, Error&) const { return IdentityStops(); } }; @@ -148,7 +155,7 @@ template struct StopsConverter> { public: template - Result> operator()(const V& value) const { + optional> operator()(const V& value, Error& error) const { std::string type = util::Interpolatable ? "exponential" : "interval"; auto typeValue = objectMember(value, "type"); @@ -156,16 +163,18 @@ public: type = *toString(*typeValue); } - optional>> result; + bool matched = false; + optional> result; // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 auto tryConvert = [&] (auto* tp) { using Stops = std::decay_t; if (type == Converter::type) { - auto stops = convert(value); - result = stops - ? Result>(*stops) - : Result>(stops.error()); + matched = true; + optional stops = convert(value, error); + if (stops) { + result = variant(*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 struct Converter> { template - Result> operator()(const V& value) const { + optional> 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::Stops>()(value); + auto stops = StopsConverter::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } return CameraFunction(*stops); @@ -199,46 +210,50 @@ struct Converter> { }; template -Result> convertDefaultValue(const V& value) { +optional> convertDefaultValue(const V& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); if (!defaultValueValue) { - return {}; + return optional(); } - auto defaultValue = convert(*defaultValueValue); + auto defaultValue = convert(*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 struct Converter> { template - Result> operator()(const V& value) const { + optional> 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::Stops>()(value); + auto stops = StopsConverter::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } - auto defaultValue = convertDefaultValue(value); + auto defaultValue = convertDefaultValue(value, error); if (!defaultValue) { - return defaultValue.error(); + return {}; } return SourceFunction(*propertyString, *stops, *defaultValue); @@ -253,29 +268,32 @@ struct CompositeValue : std::pair { template struct Converter> { template - Result> operator()(const V& value) const { + optional> 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 z = convert(*zoomValue); + optional z = convert(*zoomValue, error); if (!z) { - return z.error(); + return {}; } - Result s = convert(*propertyValue); + optional s = convert(*propertyValue, error); if (!s) { - return s.error(); + return {}; } return CompositeValue { *z, *s }; @@ -287,10 +305,10 @@ struct Converter> { static constexpr const char * type = "exponential"; template - Result> operator()(const V& value) const { - auto stops = convertStops, T>(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops, T>(value, error); if (!stops) { - return stops.error(); + return {}; } auto base = 1.0f; @@ -313,10 +331,10 @@ struct Converter> { static constexpr const char * type = "interval"; template - Result> operator()(const V& value) const { - auto stops = convertStops, T>(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops, T>(value, error); if (!stops) { - return stops.error(); + return {}; } std::map> convertedStops; @@ -333,10 +351,10 @@ struct Converter> { static constexpr const char * type = "categorical"; template - Result> operator()(const V& value) const { - auto stops = convertStops, T>(value); + optional> operator()(const V& value, Error& error) const { + auto stops = convertStops, T>(value, error); if (!stops) { - return stops.error(); + return {}; } std::map> convertedStops; @@ -351,29 +369,32 @@ struct Converter> { template struct Converter> { template - Result> operator()(const V& value) const { + optional> 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::Stops>()(value); + auto stops = StopsConverter::Stops>()(value, error); if (!stops) { - return stops.error(); + return {}; } - auto defaultValue = convertDefaultValue(value); + auto defaultValue = convertDefaultValue(value, error); if (!defaultValue) { - return defaultValue.error(); + return {}; } return CompositeFunction(*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 -Result convertGeoJSON(const V& value); +optional 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 { template - Result operator()(const V& value) const { + optional operator()(const V& value, Error& error) const { GeoJSONOptions options; const auto maxzoomValue = objectMember(value, "maxzoom"); @@ -19,7 +19,8 @@ struct Converter { if (toNumber(*maxzoomValue)) { options.maxzoom = static_cast(*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 { if (toNumber(*bufferValue)) { options.buffer = static_cast(*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 { if (toNumber(*toleranceValue)) { options.tolerance = static_cast(*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 { 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 { if (toNumber(*clusterMaxZoomValue)) { options.clusterMaxZoom = static_cast(*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 { if (toNumber(*clusterRadiusValue)) { options.clusterRadius = static_cast(*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> { public: template - Result> operator()(const V& value) const { + optional> 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 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 type = toString(*typeValue); if (!type) { - return Error { "layer type must be a string" }; + error = { "layer type must be a string" }; + return {}; } - Result> converted; + optional> converted; if (*type == "fill") { - converted = convertVectorLayer(*id, value); + converted = convertVectorLayer(*id, value, error); } else if (*type == "line") { - converted = convertVectorLayer(*id, value); + converted = convertVectorLayer(*id, value, error); } else if (*type == "circle") { - converted = convertVectorLayer(*id, value); + converted = convertVectorLayer(*id, value, error); } else if (*type == "symbol") { - converted = convertVectorLayer(*id, value); + converted = convertVectorLayer(*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 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 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 = eachMember(*layoutValue, [&] (const std::string& k, const V& v) { + optional 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 = setPaintProperties(*layer, value); - if (error) { - return *error; + optional error_ = setPaintProperties(*layer, value); + if (error_) { + error = *error_; + return {}; } return std::move(layer); @@ -148,15 +159,17 @@ public: private: template - Result> convertVectorLayer(const std::string& id, const V& value) const { + optional> 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 source = toString(*sourceValue); if (!source) { - return Error { "layer source must be a string" }; + error = { "layer source must be a string" }; + return {}; } std::unique_ptr layer = std::make_unique(id, *source); @@ -165,41 +178,44 @@ private: if (sourceLayerValue) { optional 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 = convert(*filterValue); + optional filter = convert(*filterValue, error); if (!filter) { - return filter.error(); + return {}; } layer->setFilter(*filter); } - return std::move(layer); + return { std::move(layer) }; } template - Result> convertRasterLayer(const std::string& id, const V& value) const { + optional> 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 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(id, *source); + return { std::make_unique(id, *source) }; } template - Result> convertBackgroundLayer(const std::string& id, const V&) const { - return std::make_unique(id); + optional> convertBackgroundLayer(const std::string& id, const V&, Error&) const { + return { std::make_unique(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 setLayoutProperty(Layer& layer, const V& value) { return Error { "layer doesn't support this property" }; } - Result typedValue = convert(value); + Error error; + optional typedValue = convert(value, error); if (!typedValue) { - return typedValue.error(); + return error; } (typedLayer->*setter)(*typedValue); @@ -42,9 +43,10 @@ optional setPaintProperty(Layer& layer, const V& value, const optional typedValue = convert(value); + Error error; + optional typedValue = convert(value, error); if (!typedValue) { - return typedValue.error(); + return error; } (typedLayer->*setter)(*typedValue, klass); @@ -58,9 +60,10 @@ optional setTransition(Layer& layer, const V& value, const optional transition = convert(value); + Error error; + optional transition = convert(value, error); if (!transition) { - return transition.error(); + return error; } (typedLayer->*setter)(*transition, klass); @@ -74,9 +77,10 @@ optional setVisibility(Layer& layer, const V& value) { return {}; } - Result visibility = convert(value); + Error error; + optional visibility = convert(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 struct Converter> { template - Result> operator()(const V& value) const { + optional> operator()(const V& value, Error& error) const { if (isUndefined(value)) { - return {}; + return PropertyValue(); } else if (isObject(value)) { - Result> function = convert>(value); + optional> function = convert>(value, error); if (!function) { - return function.error(); + return {}; } - return *function; + return { *function }; } else { - Result constant = convert(value); + optional constant = convert(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> { public: template - Result> operator()(const V& value, const std::string& id) const { + optional> 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 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 - Result> convertURLOrTileset(const V& value) const { + optional> convertURLOrTileset(const V& value, Error& error) const { auto urlVal = objectMember(value, "url"); if (!urlVal) { - Result tileset = convert(value); + optional tileset = convert(value, error); if (!tileset) { - return tileset.error(); + return {}; } - return *tileset; + return { *tileset }; } optional 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 - Result> convertRasterSource(const std::string& id, - const V& value) const { - Result> urlOrTileset = convertURLOrTileset(value); + optional> convertRasterSource(const std::string& id, + const V& value, + Error& error) const { + optional> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { - return urlOrTileset.error(); + return {}; } uint16_t tileSize = util::tileSize; @@ -77,53 +83,58 @@ private: if (tileSizeValue) { optional size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits::max()) { - return Error{ "invalid tileSize" }; + error = { "invalid tileSize" }; + return {}; } tileSize = *size; } - return std::make_unique(id, std::move(*urlOrTileset), tileSize); + return { std::make_unique(id, std::move(*urlOrTileset), tileSize) }; } template - Result> convertVectorSource(const std::string& id, - const V& value) const { - Result> urlOrTileset = convertURLOrTileset(value); + optional> convertVectorSource(const std::string& id, + const V& value, + Error& error) const { + optional> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { - return urlOrTileset.error(); + return {}; } - return std::make_unique(id, std::move(*urlOrTileset)); + return { std::make_unique(id, std::move(*urlOrTileset)) }; } template - Result> convertGeoJSONSource(const std::string& id, - const V& value) const { + optional> 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 options = convert(value); + optional options = convert(value, error); if (!options) { - return options.error(); + return {}; } auto result = std::make_unique(id, *options); if (isObject(*dataValue)) { - Result geoJSON = convertGeoJSON(*dataValue); + optional 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 { public: template - Result operator()(const V& value) const { + optional 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 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 minzoom = toNumber(*minzoomValue); if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits::max()) { - return Error { "invalid minzoom" }; + error = { "invalid minzoom" }; + return {}; } result.zoomRange.min = *minzoom; } @@ -52,7 +56,8 @@ public: if (maxzoomValue) { optional maxzoom = toNumber(*maxzoomValue); if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits::max()) { - return Error { "invalid maxzoom" }; + error = { "invalid maxzoom" }; + return {}; } result.zoomRange.max = *maxzoom; } @@ -61,7 +66,8 @@ public: if (attributionValue) { optional 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 { public: template - Result operator()(const V& value) const { + optional 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)) }; } -- cgit v1.2.1