319 lines
9.2 KiB
C++
319 lines
9.2 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <type_traits>
|
|
|
|
#include <boost/function_types/parameter_types.hpp>
|
|
#include <boost/mpl/equal.hpp>
|
|
#include <boost/mpl/pop_front.hpp>
|
|
#include <boost/mpl/transform.hpp>
|
|
#include <boost/mpl/vector.hpp>
|
|
|
|
#include <folly/Conv.h>
|
|
#include <folly/Traits.h>
|
|
|
|
namespace folly {
|
|
|
|
// Auto-conversion of key/value based on callback signature, documented in
|
|
// DynamicParser.h.
|
|
namespace detail {
|
|
class IdentifyCallable {
|
|
public:
|
|
enum class Kind { Function, MemberFunction };
|
|
template <typename Fn>
|
|
constexpr static Kind getKind() {
|
|
return test<Fn>(nullptr);
|
|
}
|
|
|
|
private:
|
|
template <typename Fn>
|
|
using IsMemFn = std::is_member_pointer<decltype(&Fn::operator())>;
|
|
template <typename Fn>
|
|
constexpr static typename std::enable_if<IsMemFn<Fn>::value, Kind>::type test(
|
|
IsMemFn<Fn>*) {
|
|
return IdentifyCallable::Kind::MemberFunction;
|
|
}
|
|
template <typename>
|
|
constexpr static Kind test(...) {
|
|
return IdentifyCallable::Kind::Function;
|
|
}
|
|
};
|
|
|
|
template <IdentifyCallable::Kind, typename Fn>
|
|
struct ArgumentTypesByKind {};
|
|
template <typename Fn>
|
|
struct ArgumentTypesByKind<IdentifyCallable::Kind::MemberFunction, Fn> {
|
|
using type = typename boost::mpl::template pop_front<
|
|
typename boost::function_types::template parameter_types<decltype(
|
|
&Fn::operator())>::type>::type;
|
|
};
|
|
template <typename Fn>
|
|
struct ArgumentTypesByKind<IdentifyCallable::Kind::Function, Fn> {
|
|
using type = typename boost::function_types::template parameter_types<Fn>;
|
|
};
|
|
|
|
template <typename Fn>
|
|
using ArgumentTypes =
|
|
typename ArgumentTypesByKind<IdentifyCallable::getKind<Fn>(), Fn>::type;
|
|
|
|
// At present, works for lambdas or plain old functions, but can be
|
|
// extended. The comparison deliberately strips cv-qualifiers and
|
|
// reference, leaving that choice up to the caller.
|
|
template <typename Fn, typename... Args>
|
|
struct HasArgumentTypes
|
|
: boost::mpl::template equal<
|
|
typename boost::mpl::
|
|
transform<ArgumentTypes<Fn>, remove_cvref<boost::mpl::_1>>::type,
|
|
boost::mpl::vector<Args...>>::type {};
|
|
template <typename... Args>
|
|
using EnableForArgTypes =
|
|
typename std::enable_if<HasArgumentTypes<Args...>::value, void>::type;
|
|
|
|
// No arguments
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn>
|
|
invokeForKeyValue(Fn f, const folly::dynamic&, const folly::dynamic&) {
|
|
f();
|
|
}
|
|
|
|
// 1 argument -- pass only the value
|
|
//
|
|
// folly::dynamic (no conversion)
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
|
|
fn(v);
|
|
}
|
|
// int64_t
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
|
|
fn(v.asInt());
|
|
}
|
|
// bool
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, bool>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
|
|
fn(v.asBool());
|
|
}
|
|
// double
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, double>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
|
|
fn(v.asDouble());
|
|
}
|
|
// std::string
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic&, const folly::dynamic& v) {
|
|
fn(v.asString());
|
|
}
|
|
|
|
//
|
|
// 2 arguments -- pass both the key and the value.
|
|
//
|
|
|
|
// Pass the key as folly::dynamic, without conversion
|
|
//
|
|
// folly::dynamic, folly::dynamic (no conversion of value, either)
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic, folly::dynamic>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k, v);
|
|
}
|
|
// folly::dynamic, int64_t
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic, int64_t>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k, v.asInt());
|
|
}
|
|
// folly::dynamic, bool
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic, bool>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k, v.asBool());
|
|
}
|
|
// folly::dynamic, double
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic, double>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k, v.asDouble());
|
|
}
|
|
// folly::dynamic, std::string
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, folly::dynamic, std::string>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k, v.asString());
|
|
}
|
|
|
|
// Convert the key to std::string.
|
|
//
|
|
// std::string, folly::dynamic (no conversion of value)
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string, folly::dynamic>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asString(), v);
|
|
}
|
|
// std::string, int64_t
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string, int64_t>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asString(), v.asInt());
|
|
}
|
|
// std::string, bool
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string, bool>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asString(), v.asBool());
|
|
}
|
|
// std::string, double
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string, double>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asString(), v.asDouble());
|
|
}
|
|
// std::string, std::string
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, std::string, std::string>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asString(), v.asString());
|
|
}
|
|
|
|
// Convert the key to int64_t (good for arrays).
|
|
//
|
|
// int64_t, folly::dynamic (no conversion of value)
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t, folly::dynamic>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asInt(), v);
|
|
}
|
|
// int64_t, int64_t
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t, int64_t>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asInt(), v.asInt());
|
|
}
|
|
// int64_t, bool
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t, bool>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asInt(), v.asBool());
|
|
}
|
|
// int64_t, double
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t, double>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asInt(), v.asDouble());
|
|
}
|
|
// int64_t, std::string
|
|
template <typename Fn>
|
|
EnableForArgTypes<Fn, int64_t, std::string>
|
|
invokeForKeyValue(Fn fn, const folly::dynamic& k, const folly::dynamic& v) {
|
|
fn(k.asInt(), v.asString());
|
|
}
|
|
} // namespace detail
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::optional(const folly::dynamic& key, Fn fn) {
|
|
wrapError(&key, [&]() {
|
|
if (auto vp = value().get_ptr(key)) {
|
|
parse(key, *vp, fn);
|
|
}
|
|
});
|
|
}
|
|
|
|
//
|
|
// Implementation of DynamicParser template & inline methods.
|
|
//
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::required(const folly::dynamic& key, Fn fn) {
|
|
wrapError(&key, [&]() {
|
|
auto vp = value().get_ptr(key);
|
|
if (!vp) {
|
|
throw std::runtime_error(folly::to<std::string>(
|
|
"Couldn't find key ",
|
|
detail::toPseudoJson(key),
|
|
" in dynamic object"));
|
|
}
|
|
parse(key, *vp, fn);
|
|
});
|
|
}
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::objectItems(Fn fn) {
|
|
wrapError(nullptr, [&]() {
|
|
for (const auto& kv : value().items()) { // .items() can throw
|
|
parse(kv.first, kv.second, fn);
|
|
}
|
|
});
|
|
}
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::arrayItems(Fn fn) {
|
|
wrapError(nullptr, [&]() {
|
|
size_t i = 0;
|
|
for (const auto& v : value()) { // Iteration can throw
|
|
parse(i, v, fn); // i => dynamic cannot throw
|
|
++i;
|
|
}
|
|
});
|
|
}
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::wrapError(const folly::dynamic* lookup_k, Fn fn) {
|
|
try {
|
|
fn();
|
|
} catch (DynamicParserLogicError&) {
|
|
// When the parser is misused, we throw all the way up to the user,
|
|
// instead of reporting it as if the input is invalid.
|
|
throw;
|
|
} catch (DynamicParserParseError&) {
|
|
// We are just bubbling up a parse error for OnError::THROW.
|
|
throw;
|
|
} catch (const std::exception& ex) {
|
|
reportError(lookup_k, ex);
|
|
}
|
|
}
|
|
|
|
template <typename Fn>
|
|
void DynamicParser::parse(
|
|
const folly::dynamic& k,
|
|
const folly::dynamic& v,
|
|
Fn fn) {
|
|
auto guard = stack_.push(k, v); // User code can nest parser calls.
|
|
wrapError(nullptr, [&]() { detail::invokeForKeyValue(fn, k, v); });
|
|
}
|
|
|
|
inline const folly::dynamic& DynamicParser::ParserStack::key() const {
|
|
if (!key_) {
|
|
throw DynamicParserLogicError("Only call key() inside parsing callbacks.");
|
|
}
|
|
return *key_;
|
|
}
|
|
|
|
inline const folly::dynamic& DynamicParser::ParserStack::value() const {
|
|
if (!value_) {
|
|
throw DynamicParserLogicError(
|
|
"Parsing nullptr, or parsing after releaseErrors()");
|
|
}
|
|
return *value_;
|
|
}
|
|
|
|
} // namespace folly
|