#pragma once /**************************************************************************************** * * * MOUNT CONTROL COMPONENTS LIBRARY * * * * * * IMPLEMENTATION OF DESERIALIZER CLASSES * * * ****************************************************************************************/ #include #include #include #include "mcc_concepts.h" #include "mcc_coordinate.h" #include "mcc_epoch.h" #include "mcc_error.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 : 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(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(ec), MccDeserializerCategory::get()); } template struct mcc_deserializer_interface_t { virtual ~mcc_deserializer_interface_t() = default; typedef RetT error_t; template SelfT, traits::mcc_input_char_range R, typename ValueT, typename... DeserParamsT> RetT operator()(this SelfT&& self, R const& input, ValueT& value, DeserParamsT&&... params) { return std::forward(self)(input, value); } protected: mcc_deserializer_interface_t() = default; }; template concept mcc_deserializer_c = std::derived_from>; namespace details { struct MccDeserializerBase : mcc_deserializer_interface_t, utils::mcc_elem_sequence_with_delim_t { using typename mcc_deserializer_interface_t::error_t; virtual ~MccDeserializerBase() = default; protected: MccDeserializerBase() = default; // // empty == true, if the 'input' is empty or if all elements consist of only spaces // std::vector splitAggregateValue(traits::mcc_input_char_range auto const& input, bool& empty) const { std::vector res; if (std::ranges::size(input)) { empty = true; std::ranges::for_each(std::views::split(input, this->_elementDelimiter), [&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 R, typename... DeserParamsT> error_t deserializingRange(mcc_deserializer_c auto& dsr, traits::mcc_input_char_range auto const& input, R& r, DeserParamsT&&... params) const { 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, _seqDelimiter); VT val; auto it = r.begin(); for (auto const& el : r_str) { auto err = dsr(el, val, std::forward(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; } }; } // namespace details /* fallback template: basic deserializer */ template class MccDeserializer : public details::MccDeserializerBase { public: using typename details::MccDeserializerBase::error_t; virtual ~MccDeserializer() = default; error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) { if constexpr (std::is_arithmetic_v) { auto v = mcc::utils::numFromStr(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 r; if constexpr (traits::mcc_array_c) { 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) { using el_t = std::ranges::range_value_t; static_assert(std::ranges::output_range, "INVALID RANGE TYPE!!!"); // no reference or constants allowed static_assert(std::is_reference_v || std::is_const_v, "INVALID RANGE ELEMENT TYPE!!!"); MccDeserializer dsr; return deserializingRange(dsr, input, value); } else if constexpr (traits::mcc_time_duration_c) { typename VT::rep vd; MccDeserializer dsr; auto err = dsr(trimSpaces(input), vd); 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 requires(mcc_coord_epoch_c || (std::ranges::output_range> && mcc_coord_epoch_c>>)) class MccDeserializer : public details::MccDeserializerBase { public: using typename details::MccDeserializerBase::error_t; virtual ~MccDeserializer() = default; error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) { if constexpr (mcc_coord_epoch_c) { // scalar bool ok = value.fromCharRange(input); if (!ok) { return MccDeserializerErrorCode::ERROR_INVALID_SERIALIZED_VALUE; } } else { // range using value_t = std::remove_cv_t>; MccDeserializer dsr; return deserializingRange(dsr, input, value); } } }; template requires(!std::is_arithmetic_v && (mcc_angle_c || (std::ranges::output_range> && mcc_angle_c>>))) class MccDeserializer : public details::MccDeserializerBase { public: using typename details::MccDeserializerBase::error_t; virtual ~MccDeserializer() = default; // if hms == true and the input sequence is a sexagesimal string then it interpretates angle as hours:mins:secs // otherwise the input sequence is interpretated as decimal or sexagesimal degrees error_t operator()(traits::mcc_input_char_range auto const& input, VT& value, bool hms = false) { if constexpr (mcc_angle_c) { // scalar 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; } } else { // range using value_t = std::remove_cv_t>; MccDeserializer dsr; if constexpr (std::invocable, decltype(input), VT&, bool>) { // it is assumed here, the angle deserializer has the same behavior as the one implemented above // (that is, it can interpret the third argument as a flag "false/true = only // degrees/hour-like") return deserializingRange(dsr, input, value, hms); } else { return deserializingRange(dsr, input, value); } } return MccDeserializerErrorCode::ERROR_OK; } }; template requires(mcc_skypoint_c || (std::ranges::output_range> && mcc_skypoint_c>>)) class MccDeserializer : public details::MccDeserializerBase { public: using typename details::MccDeserializerBase::error_t; virtual ~MccDeserializer() = default; error_t operator()(traits::mcc_input_char_range auto const& input, VT& value) { if constexpr (mcc_skypoint_c) { // scalar: X[elem-delim]Y{[elem-delim]TIME-POINT[elem-delim]PAIR-KIND} bool empty; // exactly 2 or >3 elements auto elems = splitAggregateValue(input, 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 dsr_ang; typename MccDeserializer::error_t dsr_err; if (elems.size() >= 4) { // X, Y, TIME-POINT, PAIR-KIND // first, get pair-kind 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; } } if (elems.size() >= 2) { // if == 2: just X and Y (if so it is interpretated as RADEC_ICRS) if (MccCoordinatePairToSxgmRep(pair_kind) == MccCoordinatePairRep::MCC_COORDPAIR_REP_SXGM_HOURDEG) { // if input X-angle is in sexagesimal from then interpretate it in hours::mins::secs format if constexpr (std::invocable, decltype(elems[0]), MccAngle&, bool>) { // it is assumed here, the angle deserializer has the same behavior as the one implemented above // (that is, it can interpret the third argument as a flag "false/true = only // degrees/hour-like") dsr_err = dsr_ang(elems[0], x, true); } else { dsr_err = dsr_ang(elems[0], x); } } else { dsr_err = dsr_ang(elems[0], x); } if (dsr_err) { return mcc_deduced_err(dsr_err, MccDeserializerErrorCode::ERROR_UNDERLYING_DESERIALIZER); } dsr_err = dsr_ang(elems[1], y); 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; } } else { // range using value_t = std::remove_cv_t>; MccDeserializer dsr; return deserializingRange(dsr, input, value); } return MccDeserializerErrorCode::ERROR_OK; } }; } // namespace mcc::impl