Files
mcc/mcc_deserializer.h
2026-02-11 18:22:06 +03:00

452 lines
15 KiB
C++

#pragma once
#include <algorithm>
#include "mcc_coordinate.h"
#include "mcc_epoch.h"
#include "mcc_serialization_common.h"
namespace mcc::impl
{
enum class MccDeserializerErrorCode : int {
ERROR_OK,
ERROR_UNDERLYING_DESERIALIZER,
ERROR_INVALID_SERIALIZED_VALUE,
ERROR_COORD_TRANSFORM
};
} // namespace mcc::impl
namespace std
{
template <>
class is_error_code_enum<mcc::impl::MccDeserializerErrorCode> : public true_type
{
};
} // namespace std
namespace mcc::impl
{
// error category
struct MccDeserializerCategory : public std::error_category {
MccDeserializerCategory() : std::error_category() {}
const char* name() const noexcept
{
return "MCC-DESERIALIZER-ERR-CATEGORY";
}
std::string message(int ec) const
{
MccDeserializerErrorCode err = static_cast<MccDeserializerErrorCode>(ec);
switch (err) {
case MccDeserializerErrorCode::ERROR_OK:
return "OK";
case MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER:
return "error returned by underlying deserializer";
case MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE:
return "invalid serialized value";
case MccDeserializerErrorCode::ERROR_COORD_TRANSFORM:
return "coordinates transformation error";
default:
return "UNKNOWN";
}
}
static const MccDeserializerCategory& get()
{
static const MccDeserializerCategory constInst;
return constInst;
}
};
inline std::error_code make_error_code(MccDeserializerErrorCode ec)
{
return std::error_code(static_cast<int>(ec), MccDeserializerCategory::get());
}
/* BASE DESERIALIZER CLASS (FOR IMPLEMENTATIONS BELOW) */
struct MccDeserializerBase : mcc_deserializer_interface_t<MccError> {
using typename mcc_deserializer_interface_t<MccError>::error_t;
virtual ~MccDeserializerBase() = default;
protected:
MccDeserializerBase() = default;
//
// empty == true, if the 'input' is empty or if all elements consist of only spaces
//
static std::vector<std::string_view> splitValueIntoElements(traits::mcc_input_char_range auto const& input,
mcc_serialization_params_c auto const& params,
bool& empty)
{
static_assert(std::ranges::contiguous_range<decltype(input)>, "NOT IMPLEMENTED FOR NON-CONTIGUIUS RANGES!!!");
std::vector<std::string_view> res;
if (std::ranges::size(input)) {
empty = true;
std::ranges::for_each(std::views::split(input, params.elem_delim), [&res, &empty](auto const& el) {
std::back_inserter(res) = utils::trimSpaces(std::string_view{el.begin(), el.end()});
if (empty && res.back().size()) {
empty = false;
}
});
} else {
empty = true;
}
return res;
}
template <typename VT, std::ranges::output_range<VT> R, typename... DeserParamsT>
static error_t deserializingRange(mcc_deserializer_c auto& dsr,
traits::mcc_input_char_range auto const& input,
R& r,
mcc_serialization_params_c auto const& params)
{
if (std::ranges::size(input) == 0) { // ignore an empty input, just return empty range?!!
r = R{};
return MccDeserializerErrorCode::ERROR_OK;
}
auto r_str = std::views::split(input, params.seq_delim);
VT val;
auto it = r.begin();
for (auto const& el : r_str) {
auto err = dsr(el, val, std::forward<DeserParamsT>(params)...);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
if (it == r.end()) {
std::back_inserter(r) = val;
it = r.end();
} else {
*it = val;
++it;
}
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
/* MAIN (FALLBACK) TEMPLATED IMPLEMENTATION */
template <typename VT>
struct MccDeserializer : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-FALLBACK-DESERIALIZER"};
virtual ~MccDeserializer() = default;
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
if constexpr (std::is_arithmetic_v<VT>) {
auto v = mcc::utils::numFromStr<VT>(utils::trimSpaces(input));
if (!v.has_value()) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
value = v.value();
} else if constexpr (mcc::traits::mcc_output_char_range<VT>) {
VT r;
if constexpr (traits::mcc_array_c<VT>) {
size_t N =
std::ranges::size(r) <= std::ranges::size(input) ? std::ranges::size(r) : std::ranges::size(input);
for (size_t i = 0; i < N; ++i) {
r[i] = input[i];
}
if (std::ranges::size(r) > N) {
for (size_t i = N; i < std::ranges::size(r); ++i) {
r[i] = '\0';
}
}
} else {
std::ranges::copy(input, std::back_inserter(r));
}
value = r;
} else if constexpr (std::ranges::range<VT>) {
using el_t = std::ranges::range_value_t<VT>;
static_assert(std::ranges::output_range<VT, el_t>, "INVALID RANGE TYPE!!!");
// no reference or constants allowed
static_assert(!(std::is_reference_v<el_t> || std::is_const_v<el_t>), "INVALID RANGE ELEMENT TYPE!!!");
MccDeserializer<el_t> dsr;
return deserializingRange<el_t>(dsr, input, value, params);
} else if constexpr (traits::mcc_time_duration_c<VT>) {
typename VT::rep vd;
MccDeserializer<typename VT::rep> dsr;
auto err = dsr(utils::trimSpaces(input), vd, params);
if (err) {
return mcc_deduced_err(err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
value = VT{vd};
} else {
static_assert(false, "UNSUPPORTED VALUE TYPE!!!");
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
/* SPECIALIZATION FOR THE SOME CONCEPTS */
template <mcc_coord_epoch_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-COORD-EPOCH-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
bool ok = value.fromCharRange(input);
if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <typename VT>
requires(!std::is_arithmetic_v<VT> && mcc_angle_c<VT>)
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-ANGLE-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
bool hms = params.angle_format == MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
auto res = utils::parsAngleString(input, hms);
if (res) { // returned angle is in degrees!!!
value = res.value() * MCC_DEGRESS_TO_RADS;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <mcc_skypoint_c VT>
struct MccDeserializer<VT> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-SKYPOINT-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator()(traits::mcc_input_char_range auto const& input,
VT& value,
ParamsT const& params = mcc_serialization_params_t{})
{
auto pars = params;
// valid format: X<elem-delim>Y[<elem-delim>TIME-POINT<elem-delim>PAIR-KIND]
// X<elem-delim>Y (assumed RADEC_ICRS and J2000.0 epoch)
bool empty;
auto elems = MccDeserializerBase::splitValueIntoElements(input, params, empty);
if (empty || (elems.size() < 2) || (elems.size() == 3)) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
MccCoordPairKind pair_kind{MccCoordPairKind::COORDS_KIND_RADEC_ICRS};
MccCelestialCoordEpoch epoch; // J2000.0
MccAngle x, y;
MccDeserializer<MccAngle> dsr_ang;
typename MccDeserializer<MccAngle>::error_t dsr_err;
if (elems.size() > 3) { // full format
// deserialize pair kind string
pair_kind = MccCoordStrToPairKind(elems[3]);
if (pair_kind == MccCoordPairKind::COORDS_KIND_UNKNOWN) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
// epoch
bool ok = epoch.fromCharRange(elems[2]);
if (!ok) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
}
// deserialize X and Y
if (params.coordpair_format == MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURDEG) {
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_HOURS;
} else {
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
}
dsr_err = dsr_ang(elems[0], x, pars);
if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
pars.angle_format = MccSerializedAngleFormat::MCC_SERIALIZED_FORMAT_SXGM_DEGS;
dsr_err = dsr_ang(elems[1], y, pars);
if (dsr_err) {
return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER);
}
switch (pair_kind) {
case MccCoordPairKind::COORDS_KIND_RADEC_ICRS:
value.from(MccSkyRADEC_ICRS{(double)x, (double)y});
break;
case MccCoordPairKind::COORDS_KIND_RADEC_OBS:
value.from(MccSkyRADEC_OBS{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_RADEC_APP:
value.from(MccSkyRADEC_APP{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_HADEC_OBS:
value.from(MccSkyHADEC_OBS{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_HADEC_APP:
value.from(MccSkyHADEC_APP{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_AZZD:
value.from(MccSkyAZZD{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_AZALT:
value.from(MccSkyAZALT{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_XY:
value.from(MccGenXY{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_GENERIC:
value.from(MccCoordPair{(double)x, (double)y, epoch});
break;
case MccCoordPairKind::COORDS_KIND_LONLAT:
value.from(MccGeoLONLAT{(double)x, (double)y});
break;
default:
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <>
struct MccDeserializer<MccCoordPairKind> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-COORDPAIR-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator ()(
traits::mcc_input_char_range auto const& input,
MccCoordPairKind& value,
ParamsT const& params = mcc_serialization_params_t{})
{
value = MccCoordStrToPairKind(input);
if (value == MccCoordPairKind::COORDS_KIND_UNKNOWN) {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <>
struct MccDeserializer<MccSerializedAngleFormatPrec> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-ANGLE-FORMAT-PREC-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator ()(
traits::mcc_input_char_range auto const& input,
MccSerializedAngleFormatPrec& value,
ParamsT const& params = mcc_serialization_params_t{})
{
// valid format: hour_prec[<params.elem_delim>deg_prec<params.elem_delim>decimals]
std::vector<uint8_t> v;
auto err = MccDeserializer<decltype(v)>{}(input, v);
if (err) {
return MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER;
}
if (v.size() > 0) {
value.hour_prec = v[0];
}
if (v.size() > 1) {
value.deg_prec = v[1];
}
if (v.size() > 2) {
value.decimals = v[2];
}
return MccDeserializerErrorCode::ERROR_OK;
}
};
template <>
struct MccDeserializer<MccSerializedCoordPairFormat> : MccDeserializerBase {
static constexpr std::string_view deserializerName{"MCC-COORDPAIR-FORMAT-DESERIALIZER"};
template <mcc_serialization_params_c ParamsT = mcc_serialization_params_t>
error_t operator ()(
traits::mcc_input_char_range auto const& input,
MccSerializedCoordPairFormat& value,
ParamsT const& params = mcc_serialization_params_t{})
{
value = MccSerializedCoordPairFormatStrToValue(input);
if (value != MccSerializedCoordPairFormat::MCC_SERIALIZED_FORMAT_UNKNOWN) {
return MccDeserializerErrorCode::ERROR_OK;
} else {
return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE;
}
}
};
} // namespace mcc::impl