forked from pz4kybsvg/Conception
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
343 lines
8.8 KiB
343 lines
8.8 KiB
#include "drake/common/yaml/yaml_node.h"
|
|
|
|
#include <stdexcept>
|
|
#include <type_traits>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
#include "drake/common/drake_assert.h"
|
|
|
|
namespace drake {
|
|
namespace yaml {
|
|
namespace internal {
|
|
namespace {
|
|
|
|
// Boilerplate for std::visit.
|
|
template <class... Ts>
|
|
struct overloaded : Ts... {
|
|
using Ts::operator()...;
|
|
};
|
|
template <class... Ts>
|
|
overloaded(Ts...) -> overloaded<Ts...>;
|
|
|
|
// Converts a type T from our variant<> into a string name for errors.
|
|
template <typename T>
|
|
std::string_view GetNiceVariantName(const T&) {
|
|
if constexpr (std::is_same_v<T, Node::ScalarData>) {
|
|
return "Scalar";
|
|
} else if constexpr (std::is_same_v<T, Node::SequenceData>) {
|
|
return "Sequence";
|
|
} else if constexpr (std::is_same_v<T, Node::MappingData>) {
|
|
return "Mapping";
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
Node::Node() = default;
|
|
|
|
Node Node::MakeScalar(std::string value) {
|
|
Node result;
|
|
result.data_ = ScalarData{std::move(value)};
|
|
return result;
|
|
}
|
|
|
|
Node Node::MakeSequence() {
|
|
Node result;
|
|
result.data_ = SequenceData{};
|
|
return result;
|
|
}
|
|
|
|
Node Node::MakeMapping() {
|
|
Node result;
|
|
result.data_ = MappingData{};
|
|
return result;
|
|
}
|
|
|
|
Node Node::MakeNull() {
|
|
Node result;
|
|
result.data_ = ScalarData{"null"};
|
|
result.tag_ = JsonSchemaTag::kNull;
|
|
return result;
|
|
}
|
|
|
|
NodeType Node::GetType() const {
|
|
return std::visit( // BR
|
|
overloaded{
|
|
[](const ScalarData&) {
|
|
return NodeType::kScalar;
|
|
},
|
|
[](const SequenceData&) {
|
|
return NodeType::kSequence;
|
|
},
|
|
[](const MappingData&) {
|
|
return NodeType::kMapping;
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
std::string_view Node::GetTypeString() const {
|
|
return std::visit(
|
|
[](auto&& data) {
|
|
return GetNiceVariantName(data);
|
|
},
|
|
data_);
|
|
}
|
|
|
|
std::string_view Node::GetTypeString(NodeType type) {
|
|
switch (type) {
|
|
case NodeType::kScalar: {
|
|
return GetNiceVariantName(ScalarData{});
|
|
}
|
|
case NodeType::kSequence: {
|
|
return GetNiceVariantName(SequenceData{});
|
|
}
|
|
case NodeType::kMapping: {
|
|
return GetNiceVariantName(MappingData{});
|
|
}
|
|
}
|
|
DRAKE_UNREACHABLE();
|
|
}
|
|
|
|
bool Node::IsScalar() const {
|
|
return std::holds_alternative<ScalarData>(data_);
|
|
}
|
|
|
|
bool Node::IsSequence() const {
|
|
return std::holds_alternative<SequenceData>(data_);
|
|
}
|
|
|
|
bool Node::IsMapping() const {
|
|
return std::holds_alternative<MappingData>(data_);
|
|
}
|
|
|
|
bool operator==(const Node& a, const Node& b) {
|
|
// We need to compare the canonical form of a tag (i.e., its string).
|
|
auto a_tag = a.GetTag();
|
|
auto b_tag = b.GetTag();
|
|
return std::tie(a_tag, a.data_, a.filename_, a.mark_) ==
|
|
std::tie(b_tag, b.data_, b.filename_, b.mark_);
|
|
}
|
|
|
|
bool operator==(const Node::Mark& a, const Node::Mark& b) {
|
|
return std::tie(a.line, a.column) == std::tie(b.line, b.column);
|
|
}
|
|
|
|
bool operator==(const Node::ScalarData& a, const Node::ScalarData& b) {
|
|
return a.scalar == b.scalar;
|
|
}
|
|
|
|
bool operator==(const Node::SequenceData& a, const Node::SequenceData& b) {
|
|
return a.sequence == b.sequence;
|
|
}
|
|
|
|
bool operator==(const Node::MappingData& a, const Node::MappingData& b) {
|
|
return a.mapping == b.mapping;
|
|
}
|
|
|
|
std::string_view Node::GetTag() const {
|
|
return std::visit( // BR
|
|
overloaded{
|
|
[](const std::string& tag) -> std::string_view {
|
|
return tag;
|
|
},
|
|
[](JsonSchemaTag tag) -> std::string_view {
|
|
switch (tag) {
|
|
case JsonSchemaTag::kNull:
|
|
return kTagNull;
|
|
case JsonSchemaTag::kBool:
|
|
return kTagBool;
|
|
case JsonSchemaTag::kInt:
|
|
return kTagInt;
|
|
case JsonSchemaTag::kFloat:
|
|
return kTagFloat;
|
|
}
|
|
DRAKE_UNREACHABLE();
|
|
},
|
|
},
|
|
tag_);
|
|
}
|
|
|
|
void Node::SetTag(JsonSchemaTag tag) {
|
|
tag_ = tag;
|
|
}
|
|
|
|
void Node::SetTag(std::string tag) {
|
|
if (tag.empty()) {
|
|
tag_ = {};
|
|
} else {
|
|
tag_ = std::move(tag);
|
|
}
|
|
}
|
|
|
|
void Node::SetFilename(std::optional<std::string> filename) {
|
|
filename_ = std::move(filename);
|
|
}
|
|
|
|
const std::optional<std::string>& Node::GetFilename() const {
|
|
return filename_;
|
|
}
|
|
|
|
void Node::SetMark(std::optional<Mark> mark) {
|
|
mark_ = mark;
|
|
}
|
|
|
|
const std::optional<Node::Mark>& Node::GetMark() const {
|
|
return mark_;
|
|
}
|
|
|
|
const std::string& Node::GetScalar() const {
|
|
return std::visit(
|
|
overloaded{
|
|
[](const ScalarData& data) -> const std::string& {
|
|
return data.scalar;
|
|
},
|
|
[](auto&& data) -> const std::string& {
|
|
throw std::logic_error(fmt::format("Cannot Node::GetScalar on a {}",
|
|
GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
const std::vector<Node>& Node::GetSequence() const {
|
|
return std::visit(
|
|
overloaded{
|
|
[](const SequenceData& data) -> const std::vector<Node>& {
|
|
return data.sequence;
|
|
},
|
|
[](auto&& data) -> const std::vector<Node>& {
|
|
throw std::logic_error(fmt::format(
|
|
"Cannot Node::GetSequence on a {}", GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
void Node::Add(Node value) {
|
|
return std::visit(
|
|
overloaded{
|
|
[&value](SequenceData& data) -> void {
|
|
data.sequence.push_back(std::move(value));
|
|
},
|
|
[](auto&& data) -> void {
|
|
throw std::logic_error(fmt::format(
|
|
"Cannot Node::Add(value) on a {}", GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
const std::map<std::string, Node>& Node::GetMapping() const {
|
|
return std::visit(
|
|
overloaded{
|
|
[](const MappingData& data) -> const std::map<std::string, Node>& {
|
|
return data.mapping;
|
|
},
|
|
[](auto&& data) -> const std::map<std::string, Node>& {
|
|
throw std::logic_error(fmt::format(
|
|
"Cannot Node::GetMapping on a {}", GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
void Node::Add(std::string key, Node value) {
|
|
return std::visit(
|
|
overloaded{
|
|
[&key, &value](MappingData& data) -> void {
|
|
const auto result =
|
|
data.mapping.insert({std::move(key), std::move(value)});
|
|
const bool inserted = result.second;
|
|
if (!inserted) {
|
|
// Our 'key' argument is now empty (because it has been
|
|
// moved-from), so for the error message we need to dig the
|
|
// existing key out of the map.
|
|
const std::string& old_key = result.first->first;
|
|
throw std::logic_error(fmt::format(
|
|
"Cannot Node::Add(key, value) using duplicate key '{}'",
|
|
old_key));
|
|
}
|
|
},
|
|
[](auto&& data) -> void {
|
|
throw std::logic_error(
|
|
fmt::format("Cannot Node::Add(key, value) on a {}",
|
|
GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
Node& Node::At(std::string_view key) {
|
|
return std::visit(
|
|
overloaded{
|
|
[key](MappingData& data) -> Node& {
|
|
return data.mapping.at(std::string{key});
|
|
},
|
|
[](auto&& data) -> Node& {
|
|
throw std::logic_error(fmt::format("Cannot Node::At(key) on a {}",
|
|
GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
void Node::Remove(std::string_view key) {
|
|
return std::visit(
|
|
overloaded{
|
|
[key](MappingData& data) -> void {
|
|
auto erased = data.mapping.erase(std::string{key});
|
|
if (!erased) {
|
|
throw std::logic_error(fmt::format(
|
|
"No such key '{}' during Node::Remove(key)", key));
|
|
}
|
|
},
|
|
[](auto&& data) -> void {
|
|
throw std::logic_error(fmt::format(
|
|
"Cannot Node::Remove(key) on a {}", GetNiceVariantName(data)));
|
|
},
|
|
},
|
|
data_);
|
|
}
|
|
|
|
std::ostream& operator<<(std::ostream& os, const Node& node) {
|
|
if (!node.GetTag().empty()) {
|
|
os << "!<" << node.GetTag() << "> ";
|
|
}
|
|
node.Visit(overloaded{
|
|
[&](const Node::ScalarData& data) {
|
|
os << '"' << data.scalar << '"';
|
|
},
|
|
[&](const Node::SequenceData& data) {
|
|
os << "[";
|
|
bool first = true;
|
|
for (const auto& child : data.sequence) {
|
|
if (!first) {
|
|
os << ", ";
|
|
}
|
|
first = false;
|
|
os << child;
|
|
}
|
|
os << "]";
|
|
},
|
|
[&](const Node::MappingData& data) {
|
|
os << "{";
|
|
bool first = true;
|
|
for (const auto& [key, child] : data.mapping) {
|
|
if (!first) {
|
|
os << ", ";
|
|
}
|
|
first = false;
|
|
os << '"' << key << '"' << ": " << child;
|
|
}
|
|
os << "}";
|
|
},
|
|
});
|
|
return os;
|
|
}
|
|
|
|
} // namespace internal
|
|
} // namespace yaml
|
|
} // namespace drake
|