LCOV - code coverage report
Current view: top level - core/data - SPDataEncodeSerenity.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 185 0.0 %
Date: 2024-05-12 00:16:13 Functions: 0 78 0.0 %

          Line data    Source code
       1             : /**
       2             : Copyright (c) 2017-2022 Roman Katuntsev <sbkarr@stappler.org>
       3             : Copyright (c) 2023 Stappler LLC <admin@stappler.dev>
       4             : 
       5             : Permission is hereby granted, free of charge, to any person obtaining a copy
       6             : of this software and associated documentation files (the "Software"), to deal
       7             : in the Software without restriction, including without limitation the rights
       8             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       9             : copies of the Software, and to permit persons to whom the Software is
      10             : furnished to do so, subject to the following conditions:
      11             : 
      12             : The above copyright notice and this permission notice shall be included in
      13             : all copies or substantial portions of the Software.
      14             : 
      15             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      16             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      17             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      18             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      19             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      20             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      21             : THE SOFTWARE.
      22             : **/
      23             : 
      24             : #ifndef STAPPLER_DATA_SPDATAENCODESERENITY_H_
      25             : #define STAPPLER_DATA_SPDATAENCODESERENITY_H_
      26             : 
      27             : #include "SPDataValue.h"
      28             : #include "SPFilesystem.h"
      29             : 
      30             : namespace STAPPLER_VERSIONIZED stappler::data::serenity {
      31             : 
      32             : bool shouldEncodePercent(char c);
      33             : 
      34             : template <typename StringType>
      35           0 : inline void encodeString(const Callback<void(StringView)> &stream, const StringType &str) {
      36           0 :         for (auto &i : str) {
      37           0 :                 if (shouldEncodePercent(i)) {
      38           0 :                         stream << '%' << base16::charToHex(i);
      39             :                 } else {
      40           0 :                         stream << i;
      41             :                 }
      42             :         }
      43           0 : }
      44             : 
      45             : template <typename Interface>
      46             : struct RawEncoder : public Interface::AllocBaseType {
      47             :         using InterfaceType = Interface;
      48             :         using ValueType = ValueTemplate<Interface>;
      49             : 
      50             :         enum Type {
      51             :                 Dict,
      52             :                 Array,
      53             :                 Plain,
      54             :         };
      55             : 
      56           0 :         inline RawEncoder(const Callback<void(StringView)> *s) : stream(s) { }
      57             : 
      58           0 :         inline void write(nullptr_t) { (*stream) << "null"; }
      59           0 :         inline void write(bool value) { (*stream) << ((value)?"true":"false"); }
      60           0 :         inline void write(int64_t value) { (*stream) << value; }
      61           0 :         inline void write(double value) { (*stream) << value; }
      62             : 
      63           0 :         inline void write(const typename ValueType::StringType &str) {
      64           0 :                 encodeString(*stream, str);
      65           0 :         }
      66             : 
      67           0 :         inline void write(const typename ValueType::BytesType &data) {
      68           0 :                 (*stream) << '~';
      69           0 :                 encodeString(*stream, data);
      70           0 :         }
      71             : 
      72           0 :         inline void onBeginArray(const typename ValueType::ArrayType &arr) {
      73           0 :                 if (type == Type::Dict) {
      74           0 :                         type = Type::Plain;
      75             :                 } else {
      76           0 :                         type = Type::Array;
      77           0 :                         (*stream) << "~(";
      78             :                 }
      79           0 :                 preventKey = false;
      80           0 :         }
      81             : 
      82           0 :         inline void onEndArray(const typename ValueType::ArrayType &arr) {
      83           0 :                 if (type != Type::Plain) {
      84           0 :                         (*stream) << ')';
      85           0 :                         preventKey = true;
      86             :                 } else {
      87           0 :                         preventKey = false;
      88             :                 }
      89           0 :         }
      90             : 
      91           0 :         inline void onBeginDict(const typename ValueType::DictionaryType &dict) {
      92           0 :                 (*stream) << '(';
      93           0 :                 type = Type::Dict;
      94           0 :                 preventKey = false;
      95           0 :         }
      96           0 :         inline void onEndDict(const typename ValueType::DictionaryType &dict) {
      97           0 :                 (*stream) << ')';
      98           0 :                 preventKey = true;
      99           0 :         }
     100           0 :         inline void onKey(const typename ValueType::StringType &str) { write(str); }
     101           0 :         inline void onNextValue() {
     102           0 :                 if (!preventKey) {
     103           0 :                         (*stream) << ((type == Type::Dict)?';':',');
     104             :                 } else {
     105           0 :                         preventKey = false;
     106             :                 }
     107           0 :         }
     108             : 
     109           0 :         inline void onArrayValue(const ValueType &val) {
     110           0 :                 auto tmpType = type;
     111           0 :                 val.encode(*this);
     112           0 :                 type = tmpType;
     113           0 :         }
     114             : 
     115           0 :         inline void onKeyValuePair(const typename ValueType::StringType &key, const ValueType &val) {
     116           0 :                 auto tmpType = type;
     117           0 :                 onKey(key);
     118           0 :                 if (!val.isBool() || !val.asBool()) {
     119           0 :                         if (!val.isDictionary()) {
     120           0 :                                 (*stream) << ':';
     121             :                         }
     122           0 :                         if (val.isArray() && val.size() < 2) {
     123           0 :                                 type = Type::Plain; // prevent plain array
     124             :                         }
     125           0 :                         val.encode(*this);
     126             :                 } else {
     127           0 :                         (*stream) << ":true";
     128             :                 }
     129           0 :                 type = tmpType;
     130           0 :         }
     131             : 
     132             :         bool preventKey = false;
     133             :         const Callback<void(StringView)> *stream;
     134             :         Type type = Type::Dict;
     135             : };
     136             : 
     137             : template <typename Interface>
     138             : struct PrettyEncoder : public Interface::AllocBaseType {
     139             :         using InterfaceType = Interface;
     140             :         using ValueType = ValueTemplate<Interface>;
     141             : 
     142             :         enum Type {
     143             :                 Dict,
     144             :                 Array,
     145             :                 Plain,
     146             :         };
     147             : 
     148           0 :         PrettyEncoder(const Callback<void(StringView)> *stream) : stream(stream) { }
     149             : 
     150           0 :         void write(nullptr_t) { (*stream) << "null"; offsetted = false; }
     151           0 :         void write(bool value) { (*stream) << ((value)?"true":"false"); offsetted = false; }
     152           0 :         void write(int64_t value) { (*stream) << value; offsetted = false; }
     153           0 :         void write(double value) { (*stream) << value; offsetted = false; }
     154             : 
     155           0 :         void write(const typename ValueType::StringType &str) {
     156           0 :                 encodeString(*stream, str);
     157           0 :                 offsetted = false;
     158           0 :         }
     159             : 
     160           0 :         void write(const typename ValueType::BytesType &data) {
     161           0 :                 (*stream) << '~';
     162           0 :                 encodeString(*stream, data);
     163           0 :         }
     164             : 
     165           0 :         bool isObjectArray(const typename ValueType::ArrayType &arr) {
     166           0 :                 for (auto &it : arr) {
     167           0 :                         if (!it.isDictionary()) {
     168           0 :                                 return false;
     169             :                         }
     170             :                 }
     171           0 :                 return true;
     172             :         }
     173             : 
     174           0 :         void onBeginArray(const typename ValueType::ArrayType &arr) {
     175           0 :                 if (type == Type::Dict) {
     176           0 :                         type = Type::Plain;
     177             :                 } else {
     178           0 :                         type = Type::Array;
     179           0 :                         (*stream) << "~(";
     180             :                 }
     181             : 
     182           0 :                 if (!isObjectArray(arr)) {
     183           0 :                         ++ depth;
     184           0 :                         bstack.push_back(false);
     185           0 :                         offsetted = false;
     186             :                 } else {
     187           0 :                         bstack.push_back(true);
     188             :                 }
     189           0 :         }
     190             : 
     191           0 :         void onEndArray(const typename ValueType::ArrayType &arr) {
     192           0 :                 if (!bstack.empty()) {
     193           0 :                         if (!bstack.back()) {
     194           0 :                                 -- depth;
     195           0 :                                 (*stream) << '\n';
     196           0 :                                 for (size_t i = 0; i < depth; i++) {
     197           0 :                                         (*stream) << '\t';
     198             :                                 }
     199             :                         }
     200           0 :                         bstack.pop_back();
     201             :                 } else {
     202           0 :                         -- depth;
     203           0 :                         (*stream) << '\n';
     204           0 :                         for (size_t i = 0; i < depth; i++) {
     205           0 :                                 (*stream) << '\t';
     206             :                         }
     207             :                 }
     208           0 :                 if (type != Type::Plain) {
     209           0 :                         (*stream) << ')';
     210             :                 }
     211           0 :                 popComplex = true;
     212           0 :         }
     213             : 
     214           0 :         void onBeginDict(const typename ValueType::DictionaryType &dict) {
     215           0 :                 (*stream) << '(';
     216           0 :                 type = Type::Dict;
     217           0 :                 ++ depth;
     218           0 :         }
     219             : 
     220           0 :         void onEndDict(const typename ValueType::DictionaryType &dict) {
     221           0 :                 -- depth;
     222           0 :                 (*stream) << '\n';
     223           0 :                 for (size_t i = 0; i < depth; i++) {
     224           0 :                         (*stream) << '\t';
     225             :                 }
     226           0 :                 (*stream) << ')';
     227           0 :                 popComplex = true;
     228           0 :         }
     229             : 
     230           0 :         void onKey(const typename ValueType::StringType &str) {
     231           0 :                 (*stream) << '\n';
     232           0 :                 for (size_t i = 0; i < depth; i++) {
     233           0 :                         (*stream) << '\t';
     234             :                 }
     235           0 :                 write(str);
     236           0 :                 offsetted = true;
     237           0 :         }
     238             : 
     239           0 :         void onNextValue() {
     240           0 :                 (*stream) << ((type == Type::Dict)?';':',');
     241           0 :         }
     242             : 
     243           0 :         void onValue(const ValueType &val) {
     244           0 :                 if (depth > 0) {
     245           0 :                         if (popComplex && (val.isArray() || val.isDictionary())) {
     246           0 :                                 (*stream) << ' ';
     247             :                         } else {
     248           0 :                                 if (!offsetted) {
     249           0 :                                         (*stream) << '\n';
     250           0 :                                         for (size_t i = 0; i < depth; i++) {
     251           0 :                                                 (*stream) << '\t';
     252             :                                         }
     253           0 :                                         offsetted = true;
     254             :                                 }
     255             :                         }
     256           0 :                         popComplex = false;
     257             :                 }
     258           0 :         }
     259             : 
     260           0 :         inline void onArrayValue(const ValueType &val) {
     261           0 :                 auto tmpType = type;
     262           0 :                 val.encode(*this);
     263           0 :                 type = tmpType;
     264           0 :         }
     265             : 
     266           0 :         inline void onKeyValuePair(const typename ValueType::StringType &key, const ValueType &val) {
     267           0 :                 auto tmpType = type;
     268           0 :                 onKey(key);
     269           0 :                 if (!val.isBool() || !val.asBool()) {
     270           0 :                         (*stream) << ' ';
     271           0 :                         if (!val.isDictionary()) {
     272           0 :                                 (*stream) << ": ";
     273             :                         }
     274           0 :                         if (val.isArray() && val.size() < 2) {
     275           0 :                                 type = Type::Plain; // prevent plain array
     276             :                         }
     277           0 :                         val.encode(*this);
     278             :                 } else {
     279           0 :                         (*stream) << ": true";
     280             :                 }
     281           0 :                 type = tmpType;
     282           0 :         }
     283             : 
     284             :         size_t depth = 0;
     285             :         bool popComplex = false;
     286             :         bool offsetted = false;
     287             :         const Callback<void(StringView)> *stream;
     288             :         typename Interface::template ArrayType<bool> bstack;
     289             :         Type type = Type::Dict;
     290             : };
     291             : 
     292             : template <typename Interface>
     293           0 : inline void write(const Callback<void(StringView)> &stream, const ValueTemplate<Interface> &val, bool pretty) {
     294           0 :         if (pretty) {
     295           0 :                 PrettyEncoder<Interface> encoder(&stream);
     296           0 :                 val.encode(encoder);
     297           0 :         } else {
     298           0 :                 RawEncoder<Interface> encoder(&stream);
     299           0 :                 val.encode(encoder);
     300             :         }
     301           0 : }
     302             : 
     303             : template <typename Interface>
     304           0 : inline auto write(const ValueTemplate<Interface> &val, bool pretty = false) -> typename Interface::StringType {
     305           0 :         typename Interface::StringType stream;
     306           0 :         write<Interface>([&] (StringView str) {
     307           0 :                 stream.append(str.data(), str.size());
     308             :         }, val, pretty);
     309           0 :         return stream;
     310           0 : }
     311             : 
     312             : template <typename Interface>
     313           0 : bool save(const ValueTemplate<Interface> &val, StringView ipath, bool pretty) {
     314           0 :         auto path = filesystem::native::posixToNative<Interface>(ipath);
     315           0 :         std::ofstream stream(path.data());
     316           0 :         if (stream.is_open()) {
     317           0 :                 write([&] (StringView str) {
     318           0 :                         stream.write(str.data(), str.size());
     319             :                 }, val, pretty);
     320           0 :                 stream.flush();
     321           0 :                 stream.close();
     322           0 :                 return true;
     323             :         }
     324           0 :         return false;
     325           0 : }
     326             : 
     327             : template <typename Interface>
     328             : auto toString(const ValueTemplate<Interface> &data, bool pretty) -> typename Interface::StringType {
     329             :         return write(data, pretty);
     330             : }
     331             : 
     332             : }
     333             : 
     334             : #endif /* STAPPLER_DATA_SPDATAENCODESERENITY_H_ */

Generated by: LCOV version 1.14