sapling/eden/fs/config/FieldConverter.h
Xavier Deguillard 784afd8939 config: add a clone.default-mount-protocol config
Summary:
This newly added config controls the default behavior of `eden clone` and
whether a new clone will use NFS or FUSE. This is intended to facilitate the
transition to NFS from FUSE on macOS.

Differential Revision: D30110056

fbshipit-source-id: ea6b493aa803297952b46434f6d11d8edf58e40b
2021-08-04 19:31:05 -07:00

209 lines
6.2 KiB
C++

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This software may be used and distributed according to the terms of the
* GNU General Public License version 2.
*/
#pragma once
#include <cpptoml.h>
#include <chrono>
#include <map>
#include <optional>
#include <sstream>
#include <string>
#include <type_traits>
#include <re2/re2.h>
#include <folly/Expected.h>
#include <folly/Range.h>
#include <folly/String.h>
#include "eden/fs/config/MountProtocol.h"
#include "eden/fs/utils/PathFuncs.h"
namespace facebook::eden {
/**
* Converters are used to convert strings into ConfigSettings. For example,
* they are used to convert the string settings of configuration files.
*/
template <typename T, typename Enable = void>
class FieldConverter {};
template <>
class FieldConverter<AbsolutePath> {
public:
/**
* Convert the passed string piece to an AbsolutePath.
* @param convData is a map of conversion data that can be used by conversions
* method (for example $HOME value.)
* @return the converted AbsolutePath or an error message.
*/
folly::Expected<AbsolutePath, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const;
std::string toDebugString(const AbsolutePath& path) const {
return path.value();
}
};
template <>
class FieldConverter<std::string> {
public:
folly::Expected<std::string, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const;
std::string toDebugString(const std::string& value) const {
return value;
}
};
template <typename T>
class FieldConverter<std::optional<T>> {
public:
folly::Expected<std::optional<T>, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const {
return FieldConverter<T>{}.fromString(value, convData);
}
std::string toDebugString(const std::optional<T>& value) const {
return FieldConverter<T>{}.toDebugString(value.value_or(T{}));
}
};
template <typename T>
class FieldConverter<std::vector<T>> {
public:
folly::Expected<std::vector<T>, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const {
// make the array parsable by cpptoml
std::string kArrayKeyName{"array"};
std::istringstream valueStream{
folly::to<std::string>(kArrayKeyName, " = ", value.str())};
// parse in toml type
std::shared_ptr<cpptoml::array> elements;
try {
cpptoml::parser parser{valueStream};
auto table = parser.parse();
elements = table->get_array(kArrayKeyName);
} catch (cpptoml::parse_exception& err) {
return folly::Unexpected<std::string>(folly::to<std::string>(
"Error parsing an array of strings: ", err.what()));
}
// parse from toml type to eden type
std::vector<T> deserializedElements;
deserializedElements.reserve(elements->get().size());
for (auto& element : *elements) {
auto stringElement = cpptoml::get_impl<std::string>(element);
if (stringElement) {
auto deserializedElement =
FieldConverter<T>{}.fromString(*stringElement, convData);
if (deserializedElement.hasValue()) {
deserializedElements.push_back(deserializedElement.value());
} else {
return folly::Unexpected(deserializedElement.error());
}
} else {
return folly::Unexpected<std::string>(
"eden currenly only supports lists of strings for config values");
}
}
return deserializedElements;
}
std::string toDebugString(const std::vector<T>& value) const {
std::vector<std::string> serializedElements;
serializedElements.resize(value.size());
std::transform(
value.begin(),
value.end(),
serializedElements.begin(),
[](auto& element) {
return FieldConverter<T>{}.toDebugString(element);
});
return folly::join(", ", serializedElements);
}
};
/*
* FieldConverter implementation for integers, floating point, and bool types
*/
template <typename T>
class FieldConverter<
T,
typename std::enable_if<std::is_arithmetic<T>::value>::type> {
public:
/**
* Convert the passed string piece to a boolean.
* @param convData is a map of conversion data that can be used by conversions
* method (for example $HOME value.)
* @return the converted boolean or an error message.
*/
folly::Expected<T, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& /* convData */) const {
auto result = folly::tryTo<T>(value);
if (result.hasValue()) {
return result.value();
}
return folly::makeUnexpected<std::string>(
folly::makeConversionError(result.error(), value).what());
}
std::string toDebugString(T value) const {
if constexpr (std::is_same<T, bool>::value) {
return value ? "true" : "false";
}
return folly::to<std::string>(value);
}
};
/*
* FieldConverter implementation for nanoseconds.
*
* We could fairly easily implement this for other duration types, but we would
* have to decide what to do if the config specifies a more granular input
* value. e.g., if we wanted to parse a config field as `std::chrono::minutes`
* what should we do if the value in the config file was "10s"?
*/
template <>
class FieldConverter<std::chrono::nanoseconds> {
public:
folly::Expected<std::chrono::nanoseconds, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const;
std::string toDebugString(std::chrono::nanoseconds value) const;
};
template <>
class FieldConverter<std::shared_ptr<re2::RE2>> {
public:
folly::Expected<std::shared_ptr<re2::RE2>, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const;
std::string toDebugString(std::shared_ptr<re2::RE2> value) const;
};
template <>
class FieldConverter<MountProtocol> {
public:
folly::Expected<MountProtocol, std::string> fromString(
folly::StringPiece value,
const std::map<std::string, std::string>& convData) const;
std::string toDebugString(MountProtocol value) const;
};
} // namespace facebook::eden