LCOV - code coverage report
Current view: top level - src - json.cpp (source / functions) Coverage Total Hit
Test: JSON Expression Parser Lines: 94.6 % 257 243
Test Date: 2024-11-04 20:34:32 Functions: 97.7 % 44 43
Branches: 61.0 % 351 214

             Branch data     Line data    Source code
       1                 :             : /*
       2                 :             :  * The MIT License (MIT)
       3                 :             :  *
       4                 :             :  * Copyright (c) 2024 Yaroslav Riabtsev <yaroslav.riabtsev@rwth-aachen.de>
       5                 :             :  *
       6                 :             :  * Permission is hereby granted, free of charge, to any person obtaining a copy
       7                 :             :  * of this software and associated documentation files (the "Software"), to deal
       8                 :             :  * in the Software without restriction, including without limitation the rights
       9                 :             :  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      10                 :             :  * copies of the Software, and to permit persons to whom the Software is
      11                 :             :  * furnished to do so, subject to the following conditions:
      12                 :             :  *
      13                 :             :  * The above copyright notice and this permission notice shall be included
      14                 :             :  * in all copies or substantial portions of the Software.
      15                 :             :  *
      16                 :             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      17                 :             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      18                 :             :  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
      19                 :             :  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      20                 :             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      21                 :             :  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      22                 :             :  * SOFTWARE.
      23                 :             :  */
      24                 :             : 
      25                 :             : #include "json.hpp"
      26                 :             : #include "reference.hpp"
      27                 :             : 
      28                 :             : #include <algorithm>
      29                 :             : #include <ranges>
      30                 :             : #include <sstream>
      31                 :             : 
      32                 :         112 : std::string json_lib::json_type_to_string(const json_type type) {
      33   [ +  +  +  +  :         112 :     switch (type) {
             +  +  +  + ]
      34                 :          16 :     case json_type::object_json:
      35         [ +  - ]:          32 :         return "JSON-Object";
      36                 :          16 :     case json_type::array_json:
      37         [ +  - ]:          32 :         return "JSON-Array";
      38                 :          16 :     case json_type::string_json:
      39         [ +  - ]:          32 :         return "JSON-String";
      40                 :          15 :     case json_type::real_json:
      41         [ +  - ]:          30 :         return "JSON-Real";
      42                 :          17 :     case json_type::integer_json:
      43         [ +  - ]:          34 :         return "JSON-Integer";
      44                 :          16 :     case json_type::boolean_json:
      45         [ +  - ]:          32 :         return "JSON-Boolean";
      46                 :          15 :     case json_type::null_json:
      47         [ +  - ]:          30 :         return "JSON-Null";
      48                 :           1 :     default:
      49         [ +  - ]:           2 :         return "Unknown JSON-Type";
      50                 :             :     }
      51                 :             : }
      52                 :             : 
      53                 :          52 : std::invalid_argument json_lib::throw_message(
      54                 :             :     const std::shared_ptr<const json>& obj1,
      55                 :             :     const std::shared_ptr<const json>& obj2, const std::source_location location
      56                 :             : ) {
      57         [ +  - ]:          52 :     std::ostringstream oss;
      58                 :             :     oss << "[Json-Error] Attempting to evaluate a "
      59   [ +  -  +  - ]:          52 :         << json_type_to_string(obj1->type()) << " by a "
      60   [ +  -  +  -  :         104 :         << json_type_to_string(obj2->type()) << ". ";
          +  -  +  -  +  
             -  +  -  +  
                      - ]
      61                 :             : #ifndef NDEBUG
      62         [ +  - ]:          52 :     oss << "Values: ";
      63                 :             :     try {
      64   [ +  +  +  - ]:          52 :         oss << obj1->to_string();
      65         [ -  + ]:           1 :     } catch (const std::runtime_error& e) {
      66   [ +  -  +  -  :           1 :         oss << "{ " << e.what() << " }";
                   +  - ]
      67                 :           1 :     }
      68         [ +  - ]:          52 :     oss << " and ";
      69                 :             :     try {
      70   [ +  +  +  - ]:          52 :         oss << obj2->to_string();
      71         [ -  + ]:           1 :     } catch (const std::runtime_error& e) {
      72   [ +  -  +  -  :           1 :         oss << "{ " << e.what() << " }";
                   +  - ]
      73                 :           1 :     }
      74         [ +  - ]:          52 :     oss << ". ";
      75   [ +  -  +  -  :          52 :     oss << "In file: " << location.file_name() << '(' << location.line() << ':'
          +  -  +  -  +  
                      - ]
      76         [ +  - ]:          52 :         << location.column() << ") in function `" << location.function_name()
      77   [ +  -  +  -  :          52 :         << "`.";
                   +  - ]
      78                 :             : #endif
      79   [ +  -  +  - ]:         104 :     return std::invalid_argument(oss.str());
      80                 :          52 : }
      81                 :             : 
      82                 :          14 : json_lib::json_boolean::json_boolean(const bool value)
      83                 :          14 :     : value(value) {
      84                 :          14 :     _type = (json_type::boolean_json);
      85                 :          14 : }
      86                 :             : 
      87                 :        2535 : json_lib::json_integer::json_integer(const int value)
      88                 :        2535 :     : value(value) {
      89                 :        2535 :     _type = (json_type::integer_json);
      90                 :        2535 : }
      91                 :             : 
      92                 :          10 : json_lib::json_real::json_real(const float value)
      93                 :          10 :     : value(value) {
      94                 :          10 :     _type = (json_type::real_json);
      95         [ +  - ]:          10 :     const std::string result = std::to_string(value);
      96                 :          10 :     size_t last = result.size() - 1;
      97         [ +  + ]:          58 :     while (result[last] == '0') {
      98                 :          48 :         --last;
      99                 :             :     }
     100         [ +  + ]:          10 :     if (result[last] == '.') {
     101                 :           8 :         ++last;
     102                 :             :     }
     103         [ +  - ]:          10 :     str_value = result.substr(0, last + 1);
     104                 :          10 : }
     105                 :             : 
     106                 :         816 : json_lib::json_real::json_real(const std::string& str_value)
     107         [ +  - ]:         816 :     : str_value(str_value) {
     108                 :         816 :     _type = (json_type::real_json);
     109                 :             :     size_t pos;
     110                 :             :     try {
     111         [ +  + ]:         816 :         value = std::stof(str_value, &pos);
     112         [ +  + ]:         811 :         if (pos != str_value.length()) {
     113         [ +  - ]:           2 :             throw std::invalid_argument("Invalid characters in float string");
     114                 :             :         }
     115      [ -  +  + ]:           7 :     } catch (const std::invalid_argument&) {
     116                 :           5 :         throw;
     117                 :           7 :     } catch (const std::out_of_range&) {
     118                 :           2 :         throw;
     119                 :           2 :     }
     120                 :         823 : }
     121                 :             : 
     122                 :        7573 : json_lib::json_string::json_string(std::string value)
     123                 :        7573 :     : value(std::move(value)) {
     124                 :        7573 :     _type = (json_type::string_json);
     125                 :        7573 : }
     126                 :             : 
     127                 :          97 : json_lib::json_array::json_array(const std::vector<std::shared_ptr<json>>& arr)
     128         [ +  - ]:          97 :     : list(arr) {
     129                 :          97 :     _type = (json_type::array_json);
     130                 :          97 : }
     131                 :             : 
     132                 :        2281 : json_lib::json_object::json_object(
     133                 :             :     const std::vector<std::pair<std::string, std::shared_ptr<json>>>& obj
     134                 :        2281 : )
     135         [ +  - ]:        2281 :     : data(obj) {
     136                 :        2281 :     _type = (json_type::object_json);
     137         [ +  + ]:       14279 :     for (size_t i = 0; i < obj.size(); ++i) {
     138   [ +  -  +  + ]:       11999 :         if (indexes.contains(obj[i].first)) {
     139                 :             :             throw std::invalid_argument(
     140   [ +  -  +  - ]:           2 :                 "key `" + obj[i].first + "` is already set"
     141         [ +  - ]:           3 :             );
     142                 :             :         }
     143         [ +  - ]:       11998 :         indexes[obj[i].first] = i;
     144                 :             :     }
     145                 :        2284 : }
     146                 :             : 
     147                 :          35 : void json_lib::json_array::touch() {
     148         [ +  + ]:          35 :     if (touched) {
     149                 :           2 :         looped = true;
     150                 :           2 :         return;
     151                 :             :     }
     152                 :          33 :     touched = true;
     153         [ +  + ]:         123 :     for (auto& child : list) {
     154   [ +  -  +  + ]:          90 :         if (child->type() == json_type::reference_json) {
     155                 :             :             const auto ref
     156                 :             :                 = std::dynamic_pointer_cast<reference_lib::json_reference>(child
     157                 :           6 :                 );
     158   [ +  -  +  - ]:           6 :             ref->set_parent(shared_from_this());
     159         [ +  - ]:           6 :             child = ref->value();
     160                 :           6 :         }
     161         [ +  - ]:          90 :         child->touch();
     162                 :             :     }
     163                 :          33 :     touched = false;
     164                 :             : }
     165                 :             : 
     166                 :          18 : void json_lib::json_object::touch() {
     167         [ +  + ]:          18 :     if (touched) {
     168                 :           4 :         looped = true;
     169                 :           4 :         return;
     170                 :             :     }
     171                 :          14 :     touched = true;
     172   [ +  -  +  -  :          40 :     for (auto& child : data | std::views::values) {
             +  -  +  + ]
     173   [ +  -  +  + ]:          26 :         if (child->type() == json_type::reference_json) {
     174                 :             :             const auto ref
     175                 :             :                 = std::dynamic_pointer_cast<reference_lib::json_reference>(child
     176                 :           9 :                 );
     177   [ +  -  +  - ]:           9 :             ref->set_parent(shared_from_this());
     178         [ +  - ]:           9 :             child = ref->value();
     179                 :           9 :         }
     180         [ +  - ]:          26 :         child->touch();
     181                 :             :     }
     182                 :          14 :     touched = false;
     183                 :             : }
     184                 :             : 
     185                 :          48 : void json_lib::json::set_root(const std::shared_ptr<json>&) { }
     186                 :             : 
     187                 :           1 : void json_lib::json_array::set_root(const std::shared_ptr<json>& item) {
     188         [ -  + ]:           1 :     if (touched) {
     189                 :           0 :         looped = true;
     190                 :           0 :         return;
     191                 :             :     }
     192                 :           1 :     touched = true;
     193         [ +  + ]:           8 :     for (auto& child : list) {
     194         [ +  - ]:           7 :         child->set_root(item);
     195   [ +  -  +  + ]:           7 :         if (child->type() == json_type::reference_json) {
     196                 :             :             const auto ref
     197                 :             :                 = std::dynamic_pointer_cast<reference_lib::json_reference>(child
     198                 :           4 :                 );
     199         [ +  - ]:           4 :             child = ref->value();
     200                 :           4 :         }
     201                 :             :     }
     202                 :           1 :     touched = false;
     203                 :             : }
     204                 :             : 
     205                 :           0 : void json_lib::json_object::set_root(const std::shared_ptr<json>& item) {
     206         [ #  # ]:           0 :     if (touched) {
     207                 :           0 :         looped = true;
     208                 :           0 :         return;
     209                 :             :     }
     210                 :           0 :     touched = true;
     211   [ #  #  #  #  :           0 :     for (auto& child : data | std::views::values) {
             #  #  #  # ]
     212         [ #  # ]:           0 :         child->set_root(item);
     213   [ #  #  #  # ]:           0 :         if (child->type() == json_type::reference_json) {
     214                 :             :             const auto ref
     215                 :             :                 = std::dynamic_pointer_cast<reference_lib::json_reference>(child
     216                 :           0 :                 );
     217         [ #  # ]:           0 :             child = ref->value();
     218                 :           0 :         }
     219                 :             :     }
     220                 :           0 :     touched = false;
     221                 :             : }
     222                 :             : 
     223                 :          29 : bool json_lib::json::empty() const { return true; }
     224                 :             : 
     225                 :           2 : bool json_lib::json_array::empty() const { return size() == 0; }
     226                 :             : 
     227                 :           1 : bool json_lib::json_object::empty() const { return size() == 0; }
     228                 :             : 
     229                 :          29 : bool json_lib::json::compact() const { return true; }
     230                 :             : 
     231                 :           9 : bool json_lib::json_array::compact() const {
     232                 :           9 :     return std::ranges::all_of(list, [](const auto& element) {
     233   [ +  -  +  + ]:          25 :         return element->compact() && element->empty();
     234                 :           9 :     });
     235                 :             : }
     236                 :             : 
     237                 :          11 : bool json_lib::json_object::compact() const {
     238                 :          11 :     return size() == 0
     239   [ +  +  +  +  :          18 :         || (size() == 1 && data.begin()->second->compact()
             +  -  +  - ]
     240   [ +  -  +  + ]:          18 :             && data.begin()->second->empty());
     241                 :             : }
     242                 :             : 
     243                 :         360 : std::string json_lib::json::to_string() const {
     244                 :         360 :     return formatted_string(false);
     245                 :             : }
     246                 :             : 
     247                 :         376 : std::string json_lib::json::formatted_string(const bool pretty) const {
     248                 :         376 :     return indented_string(0, pretty);
     249                 :             : }
     250                 :             : 
     251                 :          22 : std::string json_lib::json::indented_string(size_t, bool) const {
     252         [ +  - ]:          44 :     return "null";
     253                 :             : }
     254                 :             : 
     255                 :          50 : std::string json_lib::json_boolean::indented_string(size_t, bool) const {
     256   [ +  +  +  - ]:         100 :     return value ? "true" : "false";
     257                 :             : }
     258                 :             : 
     259                 :        1731 : std::string json_lib::json_integer::indented_string(size_t, bool) const {
     260                 :        1731 :     return std::to_string(value);
     261                 :             : }
     262                 :             : 
     263                 :         508 : std::string json_lib::json_real::indented_string(size_t, bool) const {
     264                 :         508 :     return str_value;
     265                 :             : }
     266                 :             : 
     267                 :        5077 : std::string json_lib::json_string::indented_string(size_t, bool) const {
     268         [ +  - ]:        5077 :     std::ostringstream escaped;
     269         [ +  - ]:        5077 :     escaped << "\"";
     270         [ +  + ]:      201045 :     for (const char ch : value) {
     271   [ +  +  +  +  :      195968 :         switch (ch) {
             +  +  +  + ]
     272                 :           8 :         case '\"':
     273         [ +  - ]:           8 :             escaped << "\\\"";
     274                 :           8 :             break;
     275                 :           8 :         case '\\':
     276         [ +  - ]:           8 :             escaped << "\\\\";
     277                 :           8 :             break;
     278                 :           4 :         case '\b':
     279         [ +  - ]:           4 :             escaped << "\\b";
     280                 :           4 :             break;
     281                 :           4 :         case '\f':
     282         [ +  - ]:           4 :             escaped << "\\f";
     283                 :           4 :             break;
     284                 :           6 :         case '\n':
     285         [ +  - ]:           6 :             escaped << "\\n";
     286                 :           6 :             break;
     287                 :           4 :         case '\r':
     288         [ +  - ]:           4 :             escaped << "\\r";
     289                 :           4 :             break;
     290                 :           4 :         case '\t':
     291         [ +  - ]:           4 :             escaped << "\\t";
     292                 :           4 :             break;
     293                 :      195930 :         default:
     294                 :             :             // if (ch >= 0x20 && ch <= 0x7E) {
     295         [ +  - ]:      195930 :             escaped << ch;
     296                 :             :             // } else {
     297                 :             :             //     escaped << "\\u" << std::hex << std::setw(4)
     298                 :             :             //             << std::setfill('0')
     299                 :             :             //             << (static_cast<int>(static_cast<unsigned
     300                 :             :             //             char>(ch)));
     301                 :             :             // }
     302                 :      195930 :             break;
     303                 :             :         }
     304                 :             :     }
     305                 :             : 
     306         [ +  - ]:        5077 :     escaped << "\"";
     307         [ +  - ]:       10154 :     return escaped.str();
     308                 :        5077 : }
     309                 :             : 
     310                 :         612 : std::string json_lib::json_array::format_item(
     311                 :             :     const std::shared_ptr<json>& item, const size_t nested_level,
     312                 :             :     const bool pretty
     313                 :             : ) {
     314                 :         612 :     return item->indented_string(nested_level, pretty);
     315                 :             : }
     316                 :             : 
     317                 :          42 : std::string json_lib::json_array::indented_string(
     318                 :             :     const size_t indent_level, const bool pretty
     319                 :             : ) const {
     320         [ +  + ]:          42 :     if (looped) {
     321         [ +  - ]:           2 :         throw std::runtime_error("object is looped");
     322                 :             :     }
     323                 :             :     return '['
     324         [ +  - ]:          80 :         + format_container(
     325   [ +  +  +  -  :          40 :                list, format_item, indent_level, pretty && !compact()
             +  +  +  - ]
     326                 :             :         )
     327         [ +  - ]:          80 :         + "]";
     328                 :             : }
     329                 :             : 
     330                 :        8022 : std::string json_lib::json_object::format_item(
     331                 :             :     const std::pair<std::string, std::shared_ptr<json>>& item,
     332                 :             :     const size_t nested_level, const bool pretty
     333                 :             : ) {
     334         [ +  - ]:       16044 :     return "\"" + item.first
     335   [ +  -  +  -  :       24066 :         + "\": " + item.second->indented_string(nested_level, pretty);
                   +  - ]
     336                 :             : }
     337                 :             : 
     338                 :        1536 : std::string json_lib::json_object::indented_string(
     339                 :             :     const size_t indent_level, const bool pretty
     340                 :             : ) const {
     341         [ +  + ]:        1536 :     if (looped) {
     342         [ +  - ]:           3 :         throw std::runtime_error("object is looped");
     343                 :             :     }
     344                 :             :     return '{'
     345         [ +  - ]:        3066 :         + format_container(
     346   [ +  +  +  -  :        1533 :                data, format_item, indent_level, pretty && !compact()
             +  +  +  - ]
     347                 :             :         )
     348         [ +  - ]:        3066 :         + "}";
     349                 :             : }
     350                 :             : 
     351                 :             : std::shared_ptr<json_lib::json>
     352                 :          52 : json_lib::json::by(const std::shared_ptr<json>& item) const {
     353   [ +  -  +  - ]:          52 :     throw throw_message(shared_from_this(), item);
     354                 :             : }
     355                 :             : 
     356                 :          52 : int json_lib::json_integer::as_index() const { return value; }
     357                 :             : 
     358                 :             : std::shared_ptr<json_lib::json>
     359                 :           8 : json_lib::json_integer::by(const std::shared_ptr<json>& item) const {
     360   [ +  +  +  -  :           8 :     if (enable_symmetric_indexing && item->type() == json_type::array_json) {
                   +  + ]
     361                 :           1 :         const auto array = std::dynamic_pointer_cast<json_array>(item);
     362         [ +  - ]:           1 :         return array->at(as_index());
     363                 :           1 :     }
     364                 :           7 :     return json::by(item);
     365                 :             : }
     366                 :             : 
     367                 :          60 : std::string json_lib::json_string::as_key() const { return value; }
     368                 :             : 
     369                 :             : std::shared_ptr<json_lib::json>
     370                 :           9 : json_lib::json_string::by(const std::shared_ptr<json>& item) const {
     371   [ +  +  +  -  :           9 :     if (enable_symmetric_indexing && item->type() == json_type::object_json) {
                   +  + ]
     372                 :           1 :         const auto object = std::dynamic_pointer_cast<json_object>(item);
     373   [ +  -  +  - ]:           2 :         return object->at(as_key());
     374                 :           1 :     }
     375                 :           8 :     return json::by(item);
     376                 :             : }
     377                 :             : 
     378                 :          66 : size_t json_lib::json_array::size() const { return list.size(); }
     379                 :             : 
     380                 :          53 : std::shared_ptr<json_lib::json> json_lib::json_array::at(const int index
     381                 :             : ) const {
     382                 :          53 :     auto absolute_index = static_cast<size_t>(index);
     383   [ +  +  +  - ]:          53 :     if (enable_negative_indexing && index < 0) {
     384                 :           5 :         absolute_index += size();
     385                 :             :     }
     386         [ +  + ]:          53 :     if (absolute_index < size()) {
     387                 :          48 :         return list.at(absolute_index);
     388                 :             :     }
     389         [ +  - ]:           5 :     throw std::out_of_range("index out of range");
     390                 :             : }
     391                 :             : 
     392                 :             : std::shared_ptr<json_lib::json>
     393                 :          45 : json_lib::json_array::by(const std::shared_ptr<json>& item) const {
     394         [ +  + ]:          45 :     if (item->type() == json_type::integer_json) {
     395                 :          38 :         const auto number = std::dynamic_pointer_cast<json_integer>(item);
     396         [ +  + ]:          38 :         return at(number->as_index());
     397                 :          38 :     }
     398                 :           7 :     return json::by(item);
     399                 :             : }
     400                 :             : 
     401                 :          27 : size_t json_lib::json_object::size() const { return data.size(); }
     402                 :             : 
     403                 :           1 : std::vector<std::string> json_lib::json_object::get_keys() {
     404         [ +  - ]:           1 :     if (keys.size() != size()) {
     405         [ +  - ]:           1 :         auto result = std::views::keys(indexes);
     406   [ +  -  +  -  :           3 :         keys = { result.begin(), result.end() };
                   +  - ]
     407                 :             :     }
     408                 :           1 :     return keys;
     409                 :             : }
     410                 :             : 
     411                 :          64 : std::shared_ptr<json_lib::json> json_lib::json_object::at(const std::string& key
     412                 :             : ) const {
     413         [ +  + ]:          64 :     if (indexes.contains(key)) {
     414                 :          60 :         return data.at(indexes.at(key)).second;
     415                 :             :     }
     416         [ +  - ]:           4 :     throw std::out_of_range("key not found");
     417                 :             : }
     418                 :             : 
     419                 :             : std::shared_ptr<json_lib::json>
     420                 :          65 : json_lib::json_object::by(const std::shared_ptr<json>& item) const {
     421         [ +  + ]:          65 :     if (item->type() == json_type::string_json) {
     422                 :          57 :         const auto string = std::dynamic_pointer_cast<json_string>(item);
     423   [ +  -  +  + ]:          59 :         return at(string->as_key());
     424                 :          57 :     }
     425                 :           8 :     return json::by(item);
     426                 :             : }
        

Generated by: LCOV version 2.0-1