LCOV - code coverage report
Current view: top level - spb-proto-compiler/dumper - header.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 92.8 % 320 297
Test Date: 2025-05-23 14:18:14 Functions: 97.2 % 36 35

            Line data    Source code
       1              : /***************************************************************************\
       2              : * Name        : C++ header dumper                                           *
       3              : * Description : generate C++ header with all structs and enums              *
       4              : * Author      : antonin.kriz@gmail.com                                      *
       5              : * ------------------------------------------------------------------------- *
       6              : * This is free software; you can redistribute it and/or modify it under the *
       7              : * terms of the MIT license. A copy of the license can be found in the file  *
       8              : * "LICENSE" at the root of this distribution.                               *
       9              : \***************************************************************************/
      10              : 
      11              : #include "header.h"
      12              : #include "../parser/char_stream.h"
      13              : #include "ast/ast.h"
      14              : #include "ast/proto-common.h"
      15              : #include "ast/proto-field.h"
      16              : #include "ast/proto-file.h"
      17              : #include "ast/proto-import.h"
      18              : #include "ast/proto-message.h"
      19              : #include "io/file.h"
      20              : #include "parser/options.h"
      21              : #include "parser/parser.h"
      22              : #include <algorithm>
      23              : #include <array>
      24              : #include <cctype>
      25              : #include <functional>
      26              : #include <initializer_list>
      27              : #include <spb/json/deserialize.hpp>
      28              : #include <sstream>
      29              : #include <stdexcept>
      30              : #include <string>
      31              : #include <string_view>
      32              : 
      33              : using namespace std::literals;
      34              : 
      35              : namespace
      36              : {
      37              : 
      38              : using cpp_includes = std::set< std::string >;
      39              : 
      40         2252 : void dump_comment( std::ostream & stream, const proto_comment & comment )
      41              : {
      42         4779 :     for( const auto & comm : comment.comments )
      43              :     {
      44         2527 :         if( comm.starts_with( "//[[" ) )
      45              :         {
      46              :             //- ignore options in comments
      47          137 :             continue;
      48              :         }
      49              : 
      50         2390 :         stream << comm;
      51         2390 :         if( comm.back( ) != '\n' )
      52              :         {
      53         2390 :             stream << '\n';
      54              :         }
      55              :     }
      56         2252 : }
      57              : 
      58           88 : auto trim_include( std::string_view str ) -> std::string
      59              : {
      60           88 :     auto p_begin = str.data( );
      61           88 :     auto p_end   = str.data( ) + str.size( );
      62           88 :     while( p_begin < p_end && isspace( *p_begin ) )
      63              :     {
      64            0 :         p_begin++;
      65              :     }
      66              : 
      67           88 :     while( p_begin < p_end && isspace( p_end[ -1 ] ) )
      68              :     {
      69            0 :         p_end--;
      70              :     }
      71              : 
      72           88 :     if( p_begin == p_end )
      73              :     {
      74            0 :         return { };
      75              :     }
      76              : 
      77           88 :     auto add_prefix  = *p_begin != '"' && *p_begin != '<';
      78           88 :     auto add_postfix = p_end[ -1 ] != '"' && p_end[ -1 ] != '>';
      79              : 
      80           88 :     if( add_prefix || add_postfix )
      81              :     {
      82            0 :         return '"' + std::string( str ) + '"';
      83              :     }
      84          176 :     return std::string( str );
      85              : }
      86              : 
      87           12 : void dump_includes( std::ostream & stream, const cpp_includes & includes )
      88              : {
      89          100 :     for( auto & include : includes )
      90              :     {
      91           88 :         auto file = trim_include( include );
      92           88 :         if( !file.empty( ) )
      93              :         {
      94           88 :             stream << "#include " << file << "\n";
      95              :         }
      96           88 :     }
      97           12 :     stream << "\n";
      98           12 : }
      99              : 
     100          316 : auto contains_map( const proto_messages & messages ) -> bool
     101              : {
     102          619 :     for( const auto & message : messages )
     103              :     {
     104          306 :         if( !message.maps.empty( ) )
     105              :         {
     106            3 :             return true;
     107              :         }
     108              : 
     109          304 :         if( contains_map( message.messages ) )
     110              :         {
     111            1 :             return true;
     112              :         }
     113              :     }
     114          313 :     return false;
     115              : }
     116              : 
     117          380 : auto contains_oneof( const proto_messages & messages ) -> bool
     118              : {
     119          745 :     for( const auto & message : messages )
     120              :     {
     121          371 :         if( !message.oneofs.empty( ) )
     122              :         {
     123            6 :             return true;
     124              :         }
     125              : 
     126          368 :         if( contains_oneof( message.messages ) )
     127              :         {
     128            3 :             return true;
     129              :         }
     130              :     }
     131          374 :     return false;
     132              : }
     133              : 
     134           12 : void get_std_includes( cpp_includes & includes, const proto_file & file )
     135              : {
     136           24 :     includes.insert( "<spb/json.hpp>" );
     137           24 :     includes.insert( "<spb/pb.hpp>" );
     138           24 :     includes.insert( "<cstdint>" );
     139           12 :     includes.insert( "<cstddef>" );
     140              : 
     141           12 :     if( contains_map( file.package.messages ) )
     142              :     {
     143            4 :         includes.insert( "<map>" );
     144              :     }
     145           12 :     if( contains_oneof( file.package.messages ) )
     146              :     {
     147            6 :         includes.insert( "<variant>" );
     148              :     }
     149           12 : }
     150              : 
     151         1313 : void get_include_from_options( cpp_includes & includes, const proto_options & options,
     152              :                                const proto_options & message_options,
     153              :                                const proto_options & file_options, std::string_view option_include )
     154              : {
     155         1313 :     if( auto include = options.find( option_include ); include != options.end( ) )
     156              :     {
     157            2 :         includes.insert( std::string( include->second ) );
     158            2 :         return;
     159              :     }
     160         1311 :     if( auto include = message_options.find( option_include ); include != message_options.end( ) )
     161              :     {
     162            0 :         includes.insert( std::string( include->second ) );
     163            0 :         return;
     164              :     }
     165         1311 :     if( auto include = file_options.find( option_include ); include != file_options.end( ) )
     166              :     {
     167         1311 :         includes.insert( std::string( include->second ) );
     168         1311 :         return;
     169              :     }
     170              : }
     171              : 
     172         1318 : void get_includes_from_field( cpp_includes & includes, const proto_field & field,
     173              :                               const proto_message & message, const proto_file & file )
     174              : {
     175         1318 :     if( field.label == proto_field::Label::OPTIONAL )
     176              :     {
     177          840 :         get_include_from_options( includes, field.options, message.options, file.options,
     178              :                                   option_optional_include );
     179              :     }
     180         1318 :     if( field.label == proto_field::Label::REPEATED )
     181              :     {
     182          243 :         get_include_from_options( includes, field.options, message.options, file.options,
     183              :                                   option_repeated_include );
     184              :     }
     185         1318 :     if( field.label == proto_field::Label::PTR )
     186              :     {
     187            4 :         get_include_from_options( includes, field.options, message.options, file.options,
     188              :                                   option_pointer_include );
     189              :     }
     190         1318 :     if( field.type == proto_field::Type::STRING )
     191              :     {
     192          204 :         get_include_from_options( includes, field.options, message.options, file.options,
     193              :                                   option_string_include );
     194              :     }
     195         1318 :     if( field.type == proto_field::Type::BYTES )
     196              :     {
     197           22 :         get_include_from_options( includes, field.options, message.options, file.options,
     198              :                                   option_bytes_include );
     199              :     }
     200         1318 : }
     201              : 
     202          460 : void get_message_includes( cpp_includes & includes, const proto_message & message,
     203              :                            const proto_file & file )
     204              : {
     205         1663 :     for( const auto & field : message.fields )
     206              :     {
     207         1203 :         get_includes_from_field( includes, field, message, file );
     208              :     }
     209              : 
     210          464 :     for( const auto & oneof : message.oneofs )
     211              :     {
     212           15 :         for( const auto & field : oneof.fields )
     213              :         {
     214           11 :             get_includes_from_field( includes, field, message, file );
     215              :         }
     216              :     }
     217              : 
     218          512 :     for( const auto & map : message.maps )
     219              :     {
     220           52 :         get_includes_from_field( includes, map.key, message, file );
     221           52 :         get_includes_from_field( includes, map.value, message, file );
     222              :     }
     223              : 
     224          908 :     for( const auto & m : message.messages )
     225              :     {
     226          448 :         get_message_includes( includes, m, file );
     227              :     }
     228          460 : }
     229              : 
     230           12 : void get_user_includes( cpp_includes & includes, const proto_file & file )
     231              : {
     232           12 :     get_message_includes( includes, file.package, file );
     233           12 : }
     234              : 
     235           12 : void get_imports( cpp_includes & includes, const proto_file & file )
     236              : {
     237           14 :     for( const auto & import : file.file_imports )
     238              :     {
     239            2 :         includes.insert( "\"" + cpp_file_name_from_proto( import.path, ".pb.h" ).string( ) + "\"" );
     240              :     }
     241           12 : }
     242              : 
     243           12 : void dump_pragma( std::ostream & stream, const proto_file & )
     244              : {
     245           12 :     stream << "#pragma once\n\n";
     246           12 : }
     247              : 
     248           12 : void dump_syntax( std::ostream & stream, const proto_file & file )
     249              : {
     250           12 :     dump_comment( stream, file.syntax.comments );
     251           12 : }
     252              : 
     253           42 : auto type_literal_suffix( proto_field::Type type ) -> std::string_view
     254              : {
     255              :     static constexpr auto type_map =
     256              :         std::array< std::pair< proto_field::Type, std::string_view >, 12 >{ {
     257              :             { proto_field::Type::INT64, "LL" },
     258              :             { proto_field::Type::UINT32, "U" },
     259              :             { proto_field::Type::UINT64, "ULL" },
     260              :             { proto_field::Type::SINT64, "LL" },
     261              :             { proto_field::Type::FIXED32, "U" },
     262              :             { proto_field::Type::FIXED64, "ULL" },
     263              :             { proto_field::Type::SFIXED64, "LL" },
     264              :             { proto_field::Type::FLOAT, "F" },
     265              :         } };
     266              : 
     267          524 :     for( auto [ proto_type, suffix ] : type_map )
     268              :     {
     269          484 :         if( type == proto_type )
     270              :         {
     271            2 :             return suffix;
     272              :         }
     273              :     }
     274              : 
     275           40 :     return { };
     276              : }
     277              : 
     278         1203 : auto get_field_bits( const proto_field & field ) -> std::string_view
     279              : {
     280         1203 :     if( auto p_name = field.options.find( option_field_type ); p_name != field.options.end( ) )
     281              :     {
     282          114 :         auto bitfield = p_name->second;
     283          114 :         if( auto index = bitfield.find( ':' ); index != std::string_view::npos )
     284              :         {
     285           50 :             const_cast< proto_field & >( field ).bit_field = bitfield.substr( index + 1 );
     286           50 :             return bitfield.substr( index );
     287              :         }
     288              :     }
     289         1153 :     return { };
     290              : }
     291              : 
     292         1198 : auto get_container_type( const proto_options & options, const proto_options & message_options,
     293              :                          const proto_options & file_options, std::string_view option,
     294              :                          std::string_view ctype, std::string_view default_type = { } )
     295              :     -> std::string
     296              : {
     297         1198 :     if( auto p_name = options.find( option ); p_name != options.end( ) )
     298              :     {
     299            6 :         return replace( p_name->second, "$", ctype );
     300              :     }
     301              : 
     302         1192 :     if( auto p_name = message_options.find( option ); p_name != message_options.end( ) )
     303              :     {
     304            0 :         return replace( p_name->second, "$", ctype );
     305              :     }
     306              : 
     307         1192 :     if( auto p_name = file_options.find( option ); p_name != file_options.end( ) )
     308              :     {
     309         1192 :         return replace( p_name->second, "$", ctype );
     310              :     }
     311              : 
     312            0 :     return replace( default_type, "$", ctype );
     313              : }
     314              : 
     315           83 : auto get_enum_type( const proto_file & file, const proto_options & options,
     316              :                     const proto_options & message_options, const proto_options & file_options,
     317              :                     std::string_view default_type ) -> std::string_view
     318              : {
     319              :     static constexpr auto type_map =
     320              :         std::array< std::pair< std::string_view, std::string_view >, 6 >{ {
     321              :             { "int8"sv, "int8_t"sv },
     322              :             { "uint8"sv, "uint8_t"sv },
     323              :             { "int16"sv, "int16_t"sv },
     324              :             { "uint16"sv, "uint16_t"sv },
     325              :             { "int32"sv, "int32_t"sv },
     326              :         } };
     327              : 
     328           83 :     auto ctype_from_pb = [ & ]( std::string_view type )
     329              :     {
     330          385 :         for( auto [ proto_type, c_type ] : type_map )
     331              :         {
     332          385 :             if( type == proto_type )
     333              :             {
     334           83 :                 return c_type;
     335              :             }
     336              :         }
     337            0 :         throw_parse_error( file, type, "invalid enum type: " + std::string( type ) );
     338           83 :     };
     339              : 
     340           83 :     if( auto p_name = options.find( option_enum_type ); p_name != options.end( ) )
     341              :     {
     342           15 :         return ctype_from_pb( p_name->second );
     343              :     }
     344              : 
     345           68 :     if( auto p_name = message_options.find( option_enum_type ); p_name != message_options.end( ) )
     346              :     {
     347            0 :         return ctype_from_pb( p_name->second );
     348              :     }
     349              : 
     350           68 :     if( auto p_name = file_options.find( option_enum_type ); p_name != file_options.end( ) )
     351              :     {
     352           68 :         return ctype_from_pb( p_name->second );
     353              :     }
     354              : 
     355            0 :     return default_type;
     356              : }
     357              : 
     358         1318 : auto convert_to_ctype( const proto_file & file, const proto_field & field,
     359              :                        const proto_message & message = { } ) -> std::string
     360              : {
     361         1318 :     if( field.bit_type != proto_field::BitType::NONE )
     362              :     {
     363          114 :         switch( field.bit_type )
     364              :         {
     365            0 :         case proto_field::BitType::NONE:
     366            0 :             throw_parse_error( file, field.type_name, "invalid type" );
     367           24 :         case proto_field::BitType::INT8:
     368           48 :             return "int8_t";
     369           24 :         case proto_field::BitType::INT16:
     370           48 :             return "int16_t";
     371           12 :         case proto_field::BitType::INT32:
     372           24 :             return "int32_t";
     373            6 :         case proto_field::BitType::INT64:
     374           12 :             return "int64_t";
     375           18 :         case proto_field::BitType::UINT8:
     376           36 :             return "uint8_t";
     377           18 :         case proto_field::BitType::UINT16:
     378           36 :             return "uint16_t";
     379           10 :         case proto_field::BitType::UINT32:
     380           20 :             return "uint32_t";
     381            2 :         case proto_field::BitType::UINT64:
     382            4 :             return "uint64_t";
     383              :         }
     384              :     }
     385              : 
     386         1204 :     switch( field.type )
     387              :     {
     388            0 :     case proto_field::Type::NONE:
     389            0 :         throw_parse_error( file, field.type_name, "invalid type" );
     390              : 
     391          204 :     case proto_field::Type::STRING:
     392          204 :         return get_container_type( field.options, message.options, file.options, option_string_type,
     393          204 :                                    "char", "std::string" );
     394           22 :     case proto_field::Type::BYTES:
     395           22 :         return get_container_type( field.options, message.options, file.options, option_bytes_type,
     396           22 :                                    "std::byte", "std::vector<$>" );
     397          498 :     case proto_field::Type::ENUM:
     398              :     case proto_field::Type::MESSAGE:
     399          498 :         return replace( field.type_name, ".", "::" );
     400              : 
     401            8 :     case proto_field::Type::FLOAT:
     402           16 :         return "float";
     403           45 :     case proto_field::Type::DOUBLE:
     404           90 :         return "double";
     405           63 :     case proto_field::Type::BOOL:
     406          126 :         return "bool";
     407           75 :     case proto_field::Type::SFIXED32:
     408              :     case proto_field::Type::INT32:
     409              :     case proto_field::Type::SINT32:
     410          150 :         return "int32_t";
     411           48 :     case proto_field::Type::FIXED32:
     412              :     case proto_field::Type::UINT32:
     413           96 :         return "uint32_t";
     414          105 :     case proto_field::Type::SFIXED64:
     415              :     case proto_field::Type::INT64:
     416              :     case proto_field::Type::SINT64:
     417          210 :         return "int64_t";
     418          136 :     case proto_field::Type::UINT64:
     419              :     case proto_field::Type::FIXED64:
     420          272 :         return "uint64_t";
     421              :     }
     422              : 
     423            0 :     throw_parse_error( file, field.type_name, "invalid type" );
     424              : }
     425              : 
     426         1203 : void dump_field_type_and_name( std::ostream & stream, const proto_field & field,
     427              :                                const proto_message & message, const proto_file & file )
     428              : {
     429         1203 :     const auto ctype = convert_to_ctype( file, field, message );
     430              : 
     431         1203 :     switch( field.label )
     432              :     {
     433          231 :     case proto_field::Label::NONE:
     434          231 :         stream << ctype << ' ' << field.name << get_field_bits( field );
     435          231 :         return;
     436          725 :     case proto_field::Label::OPTIONAL:
     437         1450 :         stream << get_container_type( field.options, message.options, file.options,
     438          725 :                                       option_optional_type, ctype, "std::optional<$>" );
     439          725 :         break;
     440          243 :     case proto_field::Label::REPEATED:
     441          486 :         stream << get_container_type( field.options, message.options, file.options,
     442          243 :                                       option_repeated_type, ctype, "std::vector<$>" );
     443          243 :         break;
     444            4 :     case proto_field::Label::PTR:
     445            8 :         stream << get_container_type( field.options, message.options, file.options,
     446            4 :                                       option_pointer_type, ctype, "std::unique_ptr<$>" );
     447            4 :         break;
     448              :     }
     449          972 :     if( auto bitfield = get_field_bits( field ); !bitfield.empty( ) )
     450              :     {
     451            0 :         throw_parse_error( file, bitfield, "bitfield can be used only with `required` label" );
     452              :     }
     453          972 :     stream << ' ' << field.name;
     454         1203 : }
     455              : 
     456          438 : void dump_enum_field( std::ostream & stream, const proto_base & field )
     457              : {
     458          438 :     dump_comment( stream, field.comment );
     459              : 
     460          438 :     stream << field.name << " = " << field.number << ",\n";
     461          438 : }
     462              : 
     463           83 : void dump_enum( std::ostream & stream, const proto_enum & my_enum, const proto_message & message,
     464              :                 const proto_file & file )
     465              : {
     466           83 :     dump_comment( stream, my_enum.comment );
     467              : 
     468              :     stream << "enum class " << my_enum.name << " : "
     469          166 :            << get_enum_type( file, my_enum.options, message.options, file.options, "int32_t" )
     470           83 :            << "\n{\n";
     471          521 :     for( const auto & field : my_enum.fields )
     472              :     {
     473          438 :         dump_enum_field( stream, field );
     474              :     }
     475           83 :     stream << "};\n";
     476           83 : }
     477              : 
     478            4 : void dump_message_oneof( std::ostream & stream, const proto_oneof & oneof, const proto_file & file )
     479              : {
     480            4 :     dump_comment( stream, oneof.comment );
     481              : 
     482            4 :     auto put_comma = false;
     483            4 :     stream << "std::variant< ";
     484           15 :     for( const auto & field : oneof.fields )
     485              :     {
     486           11 :         if( put_comma )
     487              :         {
     488            7 :             stream << ", ";
     489              :         }
     490              : 
     491           11 :         stream << convert_to_ctype( file, field );
     492           11 :         put_comma = true;
     493              :     }
     494            4 :     stream << " > " << oneof.name << ";\n";
     495           15 : }
     496              : 
     497           52 : void dump_message_map( std::ostream & stream, const proto_map & map, const proto_file & file )
     498              : {
     499           52 :     dump_comment( stream, map.comment );
     500              : 
     501           52 :     stream << "std::map< " << convert_to_ctype( file, map.key ) << ", "
     502          156 :            << convert_to_ctype( file, map.value ) << " > ";
     503           52 :     stream << map.name << ";\n";
     504          156 : }
     505              : 
     506         1203 : void dump_default_value( std::ostream & stream, const proto_field & field )
     507              : {
     508         1203 :     if( auto p_index = field.options.find( "default" ); p_index != field.options.end( ) )
     509              :     {
     510           57 :         if( field.type == proto_field::Type::ENUM )
     511              :         {
     512           12 :             stream << " = " << replace( field.type_name, ".", "::" ) << "::" << p_index->second;
     513              :         }
     514           49 :         else if( field.type == proto_field::Type::STRING &&
     515            4 :                  ( p_index->second.size( ) < 2 || p_index->second.front( ) != '"' ||
     516            0 :                    p_index->second.back( ) != '"' ) )
     517              :         {
     518            3 :             stream << " = \"" << p_index->second << "\"";
     519              :         }
     520              :         else
     521              :         {
     522           42 :             stream << " = " << p_index->second << type_literal_suffix( field.type );
     523              :         }
     524              :     }
     525         1203 : }
     526              : 
     527         1203 : void dump_deprecated_attribute( std::ostream & stream, const proto_field & field )
     528              : {
     529         1203 :     if( auto p_index = field.options.find( "deprecated" );
     530         1203 :         p_index != field.options.end( ) && p_index->second == "true" )
     531              :     {
     532            9 :         stream << "[[deprecated]] ";
     533              :     }
     534         1203 : }
     535              : 
     536         1203 : void dump_message_field( std::ostream & stream, const proto_field & field,
     537              :                          const proto_message & message, const proto_file & file )
     538              : {
     539         1203 :     dump_comment( stream, field.comment );
     540         1203 :     dump_deprecated_attribute( stream, field );
     541         1203 :     dump_field_type_and_name( stream, field, message, file );
     542         1203 :     dump_default_value( stream, field );
     543         1203 :     stream << ";\n";
     544         1203 : }
     545              : 
     546          460 : void dump_forwards( std::ostream & stream, const forwarded_declarations & forwards )
     547              : {
     548          483 :     for( const auto & forward : forwards )
     549              :     {
     550           23 :         stream << "struct " << forward << ";\n";
     551              :     }
     552          460 :     if( !forwards.empty( ) )
     553              :     {
     554            3 :         stream << '\n';
     555              :     }
     556          460 : }
     557              : 
     558          448 : void dump_message( std::ostream & stream, const proto_message & message, const proto_file & file )
     559              : {
     560          448 :     dump_comment( stream, message.comment );
     561              : 
     562          448 :     stream << "struct " << message.name << "\n{\n";
     563              : 
     564          448 :     dump_forwards( stream, message.forwards );
     565          521 :     for( const auto & sub_enum : message.enums )
     566              :     {
     567           73 :         dump_enum( stream, sub_enum, message, file );
     568              :     }
     569              : 
     570          548 :     for( const auto & sub_message : message.messages )
     571              :     {
     572          100 :         dump_message( stream, sub_message, file );
     573              :     }
     574              : 
     575         1651 :     for( const auto & field : message.fields )
     576              :     {
     577         1203 :         dump_message_field( stream, field, message, file );
     578              :     }
     579              : 
     580          500 :     for( const auto & map : message.maps )
     581              :     {
     582           52 :         dump_message_map( stream, map, file );
     583              :     }
     584              : 
     585          452 :     for( const auto & oneof : message.oneofs )
     586              :     {
     587            4 :         dump_message_oneof( stream, oneof, file );
     588              :     }
     589              : 
     590              :     //- TODO: is this used in any way?
     591              :     // stream << "auto operator == ( const " << message.name << " & ) const noexcept ->
     592              :     // bool = default;\n"; stream << "auto operator != ( const " << message.name << " &
     593              :     // ) const noexcept -> bool = default;\n";
     594          448 :     stream << "};\n";
     595          448 : }
     596              : 
     597           12 : void dump_messages( std::ostream & stream, const proto_file & file )
     598              : {
     599           12 :     dump_forwards( stream, file.package.forwards );
     600          360 :     for( const auto & message : file.package.messages )
     601              :     {
     602          348 :         dump_message( stream, message, file );
     603              :     }
     604           12 : }
     605              : 
     606           12 : void dump_enums( std::ostream & stream, const proto_file & file )
     607              : {
     608           22 :     for( const auto & my_enum : file.package.enums )
     609              :     {
     610           10 :         dump_enum( stream, my_enum, file.package, file );
     611              :     }
     612           12 : }
     613              : 
     614           12 : void dump_package_begin( std::ostream & stream, const proto_file & file )
     615              : {
     616           12 :     dump_comment( stream, file.package.comment );
     617           12 :     if( !file.package.name.empty( ) )
     618              :     {
     619           12 :         stream << "namespace " << replace( file.package.name, ".", "::" ) << "\n{\n";
     620              :     }
     621           12 : }
     622              : 
     623           12 : void dump_package_end( std::ostream & stream, const proto_file & file )
     624              : {
     625           12 :     if( !file.package.name.empty( ) )
     626              :     {
     627           12 :         stream << "}// namespace " << replace( file.package.name, ".", "::" ) << "\n\n";
     628              :     }
     629           12 : }
     630              : }// namespace
     631              : 
     632            0 : void throw_parse_error( const proto_file & file, std::string_view at, std::string_view message )
     633              : {
     634            0 :     auto stream = spb::char_stream( file.content );
     635            0 :     stream.skip_to( at.data( ) );
     636            0 :     stream.throw_parse_error( message );
     637              : }
     638              : 
     639           12 : void dump_cpp_definitions( const proto_file & file, std::ostream & stream )
     640              : {
     641           12 :     auto includes = cpp_includes( );
     642           12 :     dump_pragma( stream, file );
     643           12 :     get_imports( includes, file );
     644           12 :     get_std_includes( includes, file );
     645           12 :     get_user_includes( includes, file );
     646           12 :     dump_includes( stream, includes );
     647           12 :     dump_syntax( stream, file );
     648           12 :     dump_package_begin( stream, file );
     649           12 :     dump_enums( stream, file );
     650           12 :     dump_messages( stream, file );
     651           12 :     dump_package_end( stream, file );
     652           12 : }
     653              : 
     654         1732 : auto replace( std::string_view input, std::string_view what, std::string_view with ) -> std::string
     655              : {
     656         1732 :     auto result = std::string( input );
     657         1732 :     auto pos    = size_t{ };
     658              : 
     659         2786 :     while( ( pos = result.find( what, pos ) ) != std::string::npos )
     660              :     {
     661         1054 :         result.replace( pos, what.size( ), with );
     662         1054 :         pos += with.size( );
     663              :     }
     664              : 
     665         1732 :     return result;
     666            0 : }
        

Generated by: LCOV version 2.0-1