8 #include <boost/assign.hpp>
9 #include <boost/range/adaptor/indexed.hpp>
13 #include <fmt/format.h>
14 #include <nlohmann/json.hpp>
18 using Json = nlohmann::json;
26 inline constexpr
bool AlwaysFalseV =
false;
33 GetMember<std::string>(
Json const& json,
char const* name,
JsonPointer const& breadcrumb) {
34 if (!json.contains(name)) {
35 throw error::MakeJsonErrorMissingValue<std::invalid_argument>(breadcrumb / name);
37 auto const& value_json = json[name];
38 if (!value_json.is_string()) {
39 throw error::MakeJsonErrorWrongType<std::invalid_argument>(
40 breadcrumb / name,
"string", value_json.type_name());
42 auto value = value_json.get<std::string>();
44 throw error::MakeJsonError<std::invalid_argument>(breadcrumb / name,
45 "empty string is invalid");
59 template <
class KeywordType>
61 if (!obj.is_object()) {
62 throw error::MakeJsonErrorWrongType<std::invalid_argument>(
63 breadcrumb,
"object", obj.type_name());
66 auto name = GetMember<std::string>(obj,
"name", breadcrumb);
67 if (!obj.contains(
"value")) {
68 throw error::MakeJsonErrorMissingValue<std::invalid_argument>(breadcrumb);
71 typename KeywordType::ValueType kw_value;
72 auto const& value = obj[
"value"];
73 if (value.is_boolean()) {
74 kw_value = value.get<
bool>();
75 }
else if (value.is_string()) {
76 kw_value = value.get<std::string>();
77 }
else if (value.is_number_float()) {
78 kw_value = value.get<
double>();
79 }
else if (value.is_number_unsigned()) {
80 kw_value = value.get<uint64_t>();
81 }
else if (value.is_number_integer()) {
82 kw_value = value.get<int64_t>();
84 throw std::invalid_argument(fmt::format(
"unsupported type: {}", value.type_name()));
87 std::optional<std::string> kw_comment;
88 if (obj.contains(
"comment")) {
89 auto const& comment = obj[
"comment"];
90 if (!comment.is_string()) {
91 throw error::MakeJsonErrorWrongType<std::invalid_argument>(
92 breadcrumb /
"comment",
"string", comment.type_name());
94 kw_comment = comment.get<std::string>();
100 LiteralKeyword ParseKeywordObject<LiteralKeyword>(
Json const& obj,
JsonPointer const& breadcrumb) {
101 assert(obj.is_object());
103 if (!obj.contains(
"value")) {
104 throw std::invalid_argument(
"field 'value' missing");
107 auto const& value = obj[
"value"];
108 if (!value.is_string()) {
109 throw std::invalid_argument(
110 fmt::format(
"literalKeyword value must be a string got: {}", value.type_name()));
112 auto const& value_str = value.get<std::string>();
113 if (value_str.length() > constants::RECORD_LENGTH) {
114 throw std::invalid_argument(
115 fmt::format(
"literalKeyword value longer than maximum keyword record. Max: {} got: {}",
117 constants::RECORD_LENGTH));
119 return LiteralKeyword(std::string_view(value_str));
126 auto parsed = Json::parse(keywords);
131 using namespace boost::assign;
132 using namespace boost::adaptors;
134 if (!keywords.is_array()) {
135 throw error::MakeJsonErrorWrongType<std::invalid_argument>(
136 breadcrumb,
"array", keywords.type_name());
139 std::vector<KeywordVariant> parsed_keywords;
140 parsed_keywords.reserve(keywords.size());
142 for (
auto const& index : keywords | indexed(0u)) {
143 auto const& kw_obj = index.value();
144 auto breadcrumb_idx = breadcrumb / index.index();
145 if (!kw_obj.is_object()) {
146 throw error::MakeJsonErrorWrongType<std::invalid_argument>(
147 breadcrumb_idx,
"object", kw_obj.type_name());
149 auto type = GetMember<std::string>(kw_obj,
"type", breadcrumb_idx);
150 if (type ==
"valueKeyword") {
151 parsed_keywords.emplace_back(ParseKeywordObject<ValueKeyword>(kw_obj, breadcrumb_idx));
152 }
else if (type ==
"esoKeyword") {
153 parsed_keywords.emplace_back(ParseKeywordObject<EsoKeyword>(kw_obj, breadcrumb_idx));
154 }
else if (type ==
"literalKeyword") {
155 parsed_keywords.emplace_back(
156 ParseKeywordObject<LiteralKeyword>(kw_obj, breadcrumb_idx));
158 throw error::MakeJsonErrorUnknownVariant<std::invalid_argument>(
159 breadcrumb_idx,
"'valueKeyword', 'esoKeyword' or 'literalKeyword'", type.c_str());
163 return parsed_keywords;
167 return std::visit([&](
auto const& val) ->
Json {
return nlohmann::json(val); }, value);
171 using Json = nlohmann::json;
173 [&](
auto const& kw) ->
Json {
174 using T = std::decay_t<decltype(kw)>;
175 if constexpr (std::is_same_v<T, ValueKeyword>) {
176 auto j = Json::object({{
"type",
"valueKeyword"},
180 j[
"comment"] = *kw.comment;
183 }
else if constexpr (std::is_same_v<T, EsoKeyword>) {
184 auto j = Json::object({{
"type",
"esoKeyword"},
188 j[
"comment"] = *kw.comment;
191 }
else if constexpr (std::is_same_v<T, LiteralKeyword>) {
192 return Json::object({{
"type",
"literalKeyword"}, {
"value", kw.GetRecord()}});
194 static_assert(AlwaysFalseV<T>,
"non-exhaustive visitor!");
201 auto arr = nlohmann::json::array();
202 for (
auto const& kw : keywords) {
Contains data structure for FITS keywords.
nlohmann::json_pointer< Json > JsonPointer
nlohmann::json SerializeJsonKeywords(std::vector< KeywordVariant > const &keywords)
SerializeJsons keyword to JSON.
KeywordType
Type of FITS keyword.
nlohmann::json SerializeJsonKeywordValue(BasicKeywordBase::ValueType const &value)
SerializeJsons the keyword value variant to JSON.
std::variant< ValueKeyword, EsoKeyword, LiteralKeyword > KeywordVariant
The different variants of keywords that are supported.
std::vector< KeywordVariant > ParseJsonKeywords(char const *keywords)
Parse and return FITS keywords.
nlohmann::json SerializeJsonKeyword(KeywordVariant const &keyword)
SerializeJsons keyword to JSON.
std::pair< Json const &, JsonPointer > GetMember(Json const &json, char const *name, JsonPointer const &breadcrumb)
std::variant< std::string, int64_t, uint64_t, double, bool > ValueType