#ifndef SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP #define SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP #include "engine/api/base_parameters.hpp" #include "engine/bearing.hpp" #include "engine/hint.hpp" #include "engine/polyline_compressor.hpp" #include #include #include #include #include namespace osrm::server::api { namespace { namespace ph = boost::phoenix; namespace qi = boost::spirit::qi; } // namespace template struct no_trailing_dot_policy : qi::real_policies { template static bool parse_dot(Iterator &first, Iterator const &last) { auto diff = std::distance(first, last); if (diff <= 0 || *first != '.') return false; static const constexpr char fmt[sizeof...(Fmt)] = {Fmt...}; if (sizeof(fmt) < static_cast(diff) && std::equal(fmt, fmt + sizeof(fmt), first + 1u)) return false; ++first; return true; } template static bool parse_exp(Iterator &, const Iterator &) { return false; } template static bool parse_exp_n(Iterator &, const Iterator &, Attribute &) { return false; } template static bool parse_nan(Iterator &, const Iterator &, Attribute &) { return false; } template static bool parse_inf(Iterator &, const Iterator &, Attribute &) { return false; } }; template struct BaseParametersGrammar : boost::spirit::qi::grammar { using json_policy = no_trailing_dot_policy; BaseParametersGrammar(qi::rule &root_rule) : BaseParametersGrammar::base_type(root_rule) { const auto add_hint = [](engine::api::BaseParameters &base_parameters, const std::vector &hint_strings) { if (!hint_strings.empty()) { std::vector location_hints(hint_strings.size()); std::transform(hint_strings.begin(), hint_strings.end(), location_hints.begin(), [](const auto &hint_string) { return engine::SegmentHint::FromBase64(hint_string); }); base_parameters.hints.push_back(engine::Hint{std::move(location_hints)}); } else { base_parameters.hints.emplace_back(std::nullopt); } }; const auto add_bearing = [](engine::api::BaseParameters &base_parameters, boost::optional> bearing_range) { std::optional bearing; if (bearing_range) { bearing = engine::Bearing{boost::fusion::at_c<0>(*bearing_range), boost::fusion::at_c<1>(*bearing_range)}; } base_parameters.bearings.push_back(bearing); }; const auto add_approach = [](engine::api::BaseParameters &base_parameters, boost::optional approach) { base_parameters.approaches.push_back(approach ? std::make_optional(*approach) : std::nullopt); }; const auto add_radius = [](engine::api::BaseParameters &base_parameters, boost::optional radius) { base_parameters.radiuses.push_back(radius ? std::make_optional(*radius) : std::nullopt); }; polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^"); base64_char = qi::char_("a-zA-Z0-9--_="); unlimited_rule = qi::lit("unlimited")[qi::_val = std::numeric_limits::infinity()]; bearing_rule = (qi::short_ > ',' > qi::short_)[qi::_val = ph::bind( [](short bearing, short range) { return osrm::engine::Bearing{bearing, range}; }, qi::_1, qi::_2)]; location_rule = (double_ > qi::lit(',') > double_)[qi::_val = ph::bind( [](double lon, double lat) { return util::Coordinate( util::toFixed(util::UnsafeFloatLongitude{lon}), util::toFixed(util::UnsafeFloatLatitude{lat})); }, qi::_1, qi::_2)]; polyline_rule = qi::as_string[qi::lit("polyline(") > +polyline_chars > ')'] [qi::_val = ph::bind([](const std::string &polyline) { return engine::decodePolyline(polyline); }, qi::_1)]; polyline6_rule = qi::as_string[qi::lit("polyline6(") > +polyline_chars > ')'] [qi::_val = ph::bind([](const std::string &polyline) { return engine::decodePolyline<1000000>(polyline); }, qi::_1)]; query_rule = ((location_rule % ';') | polyline_rule | polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; radiuses_rule = qi::lit("radiuses=") > (-(qi::double_ | unlimited_rule))[ph::bind(add_radius, qi::_r1, qi::_1)] % ';'; hints_rule = qi::lit("hints=") > (*qi::as_string[qi::repeat(engine::ENCODED_SEGMENT_HINT_SIZE)[base64_char]])[ph::bind( add_hint, qi::_r1, qi::_1)] % ';'; generate_hints_rule = qi::lit("generate_hints=") > qi::bool_[ph::bind(&engine::api::BaseParameters::generate_hints, qi::_r1) = qi::_1]; skip_waypoints_rule = qi::lit("skip_waypoints=") > qi::bool_[ph::bind(&engine::api::BaseParameters::skip_waypoints, qi::_r1) = qi::_1]; bearings_rule = qi::lit("bearings=") > (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';'; approach_type.add("unrestricted", engine::Approach::UNRESTRICTED)( "curb", engine::Approach::CURB)("opposite", engine::Approach::OPPOSITE); approach_rule = qi::lit("approaches=") > (-approach_type)[ph::bind(add_approach, qi::_r1, qi::_1)] % ';'; snapping_type.add("default", engine::api::BaseParameters::SnappingType::Default)( "any", engine::api::BaseParameters::SnappingType::Any); snapping_rule = qi::lit("snapping=") > snapping_type[ph::bind(&engine::api::BaseParameters::snapping, qi::_r1) = qi::_1]; format_type.add(".json", engine::api::BaseParameters::OutputFormatType::JSON)( ".flatbuffers", engine::api::BaseParameters::OutputFormatType::FLATBUFFERS); format_rule = -format_type[ph::bind(&engine::api::BaseParameters::format, qi::_r1) = qi::_1]; exclude_rule = qi::lit("exclude=") > (qi::as_string[+qi::char_("a-zA-Z0-9")] % ',')[ph::bind(&engine::api::BaseParameters::exclude, qi::_r1) = qi::_1]; base_rule = radiuses_rule(qi::_r1) // | hints_rule(qi::_r1) // | bearings_rule(qi::_r1) // | generate_hints_rule(qi::_r1) // | skip_waypoints_rule(qi::_r1) // | approach_rule(qi::_r1) // | exclude_rule(qi::_r1) // | snapping_rule(qi::_r1); } protected: qi::rule base_rule; qi::rule query_rule; qi::rule format_rule; qi::symbols format_type; qi::real_parser double_; private: qi::rule bearings_rule; qi::rule radiuses_rule; qi::rule hints_rule; qi::rule generate_hints_rule; qi::rule skip_waypoints_rule; qi::rule approach_rule; qi::rule exclude_rule; qi::rule bearing_rule; qi::rule location_rule; qi::rule()> polyline_rule; qi::rule()> polyline6_rule; qi::rule base64_char; qi::rule polyline_chars; qi::rule unlimited_rule; qi::rule snapping_rule; qi::symbols approach_type; qi::symbols snapping_type; }; } // namespace osrm::server::api #endif