Skip to content

Commit

Permalink
fixed tests, doc
Browse files Browse the repository at this point in the history
  • Loading branch information
tonda-kriz committed Nov 22, 2024
1 parent e55ed9b commit 1b99f73
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 74 deletions.
46 changes: 34 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![Library-coverage](https://tonda-kriz.github.io/simple-protobuf/library/coverage.svg)](https://tonda-kriz.github.io/simple-protobuf/library)
[![Compiler-coverage](https://tonda-kriz.github.io/simple-protobuf/compiler/coverage.svg)](https://tonda-kriz.github.io/simple-protobuf/compiler)

**simple data struct** serialization library for C++. With this library you can serialize and deserialize C++ structs directly to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf).
**simple data struct** serialization library for C++. With this library you can serialize and deserialize *POD* C++ structs directly to [JSON](https://json.org) or [protobuf](https://github.com/protocolbuffers/protobuf).
When used together with [etl library](https://github.com/ETLCPP/etl) it doesn't need to allocate any memory, so its suitable for embedded environments (see [extensions](doc/extensions.md)).

```CPP
Expand Down Expand Up @@ -37,9 +37,13 @@ auto john = PhoneBook::Person{
.id = 1234,
.email = "jdoe@example.com",
};
auto json = spb::json::serialize( john );
//- serialize john to json-string
auto json = spb::json::serialize< std::string >( john );
//- deserialize john from json-string
auto person = spb::json::deserialize< PhoneBook::Person >( json );
auto pb = spb::pb::serialize( john );
//- serialize john to protobuf-vector
auto pb = spb::pb::serialize< std::vector< std::byte > >( john );
//- deserialize john from protobuf-vector
auto person2 = spb::pb::deserialize< PhoneBook::Person >( pb );
//- john == person == person2
```
Expand Down Expand Up @@ -164,27 +168,45 @@ All generated messages (and enums) are using the following API [`include/spb/jso

```CPP
//- serialize message via writer (all other `serialize` are just wrappers around this one)
//- example: `auto serialized_size = spb::pb::serialize( message, my_writer );`
auto serialize( const auto & message, spb::io::writer on_write ) -> size_t;

//- return size in bytes of serialized message
//- example: `auto serialized_size = spb::pb::serialize_size( message );`
auto serialize_size( const auto & message ) -> size_t;

//- serialize message into string
auto serialize( const auto & message ) -> std::string
//- serialize message into container like std::string, std::vector, ...
//- example: `auto serialized_size = spb::pb::serialize( message, my_string );`
template < typename Message, spb::resizable_container Container >
auto serialize( const Message & message, Container & result ) -> size_t;

//- serialize message and return container like std::string, std::vector, ...
//- example: `auto my_string = spb::pb::serialize< std::string >( message );`
template < spb::resizable_container Container = std::string, typename Message >
auto serialize( const Message & message ) -> Container;

```
```CPP
//- deserialize message from reader (all other `deserialize` are just wrappers around this one)
void deserialize( auto & result, spb::io::reader on_read );
//- example: `spb::pb::deserialize( message, my_reader );`
void deserialize( auto & message, spb::io::reader on_read );
//- deserialize message from data
void deserialize( auto & message, std::string_view data )
//- deserialize message from container like std::string, std::vector, ...
//- example: `spb::pb::deserialize( message, my_string );`
template < typename Message, spb::size_container Container >
void deserialize( Message & message, const Container & protobuf );
//- return deserialized message from reader
auto deserialize< Message >( spb::io::reader on_read ) -> Message
//- return deserialized message from data
auto deserialize< Message >( std::string_view data ) -> Message
//- return deserialized message from container like std::string, std::vector, ...
//- example: `auto message = spb::pb::deserialize< Message >( my_string );`
template < typename Message, spb::size_container Container >
auto deserialize( const Container & protobuf ) -> Message;
//- return deserialized message from reader
//- example: `auto message = spb::pb::deserialize< Message >( my_reader );`
template < typename Message >
auto deserialize( spb::io::reader reader ) -> Message;
```

API is prefixed with `spb::json::` for **json** and `spb::pb::` for **protobuf**
Expand Down
2 changes: 1 addition & 1 deletion include/spb/json.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ static inline auto serialize( const Message & message, Container & result ) -> s
* @throws std::runtime_error on error
* @example `auto serialized_message = spb::json::serialize( message );`
*/
template < typename Message, spb::resizable_container Container = std::string >
template < spb::resizable_container Container = std::string, typename Message >
[[nodiscard]] static inline auto serialize( const Message & message ) -> Container
{
auto result = Container( );
Expand Down
2 changes: 1 addition & 1 deletion include/spb/pb.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ static inline auto serialize( const Message & message, Container & result ) -> s
* @throws std::runtime_error on error
* @example `auto serialized_message = spb::pb::serialize< std::vector< std::byte > >( message );`
*/
template < typename Message, spb::resizable_container Container = std::string >
template < spb::resizable_container Container = std::string, typename Message >
[[nodiscard]] static inline auto serialize( const Message & message ) -> Container
{
auto result = Container( );
Expand Down
138 changes: 79 additions & 59 deletions test/json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -683,32 +683,35 @@ TEST_CASE( "json" )
{
SUBCASE( "string" )
{
CHECK( spb::json::serialize< std::string >( "john" ) == R"("john")" );
CHECK( spb::json::serialize< std::string, std::string >( "john" ) == R"("john")" );
SUBCASE( "escaped" )
{
CHECK( spb::json::serialize< std::string >( "\"" ) == R"("\"")" );
CHECK( spb::json::serialize< std::string >( "\\" ) == R"("\\")" );
CHECK( spb::json::serialize< std::string >( "/" ) == R"("/")" );
CHECK( spb::json::serialize< std::string >( "\b" ) == R"("\b")" );
CHECK( spb::json::serialize< std::string >( "\f" ) == R"("\f")" );
CHECK( spb::json::serialize< std::string >( "\n" ) == R"("\n")" );
CHECK( spb::json::serialize< std::string >( "\r" ) == R"("\r")" );
CHECK( spb::json::serialize< std::string >( "\t" ) == R"("\t")" );
CHECK( spb::json::serialize< std::string >( "\"\\/\b\f\n\r\t" ) ==
CHECK( spb::json::serialize< std::string, std::string >( "\"" ) == R"("\"")" );
CHECK( spb::json::serialize< std::string, std::string >( "\\" ) == R"("\\")" );
CHECK( spb::json::serialize< std::string, std::string >( "/" ) == R"("/")" );
CHECK( spb::json::serialize< std::string, std::string >( "\b" ) == R"("\b")" );
CHECK( spb::json::serialize< std::string, std::string >( "\f" ) == R"("\f")" );
CHECK( spb::json::serialize< std::string, std::string >( "\n" ) == R"("\n")" );
CHECK( spb::json::serialize< std::string, std::string >( "\r" ) == R"("\r")" );
CHECK( spb::json::serialize< std::string, std::string >( "\t" ) == R"("\t")" );
CHECK( spb::json::serialize< std::string, std::string >( "\"\\/\b\f\n\r\t" ) ==
R"("\"\\/\b\f\n\r\t")" );
CHECK( spb::json::serialize< std::string >( "\"hello\t" ) == R"("\"hello\t")" );
CHECK( spb::json::serialize< std::string, std::string >( "\"hello\t" ) ==
R"("\"hello\t")" );
}
SUBCASE( "optional" )
{
CHECK( spb::json::serialize< std::optional< std::string > >( std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< std::string > >( "hello" ) ==
R"("hello")" );
CHECK( spb::json::serialize< std::string, std::optional< std::string > >(
std::nullopt ) == "" );
CHECK( spb::json::serialize< std::string, std::optional< std::string > >(
"hello" ) == R"("hello")" );
}
SUBCASE( "repeated" )
{
CHECK( spb::json::serialize< std::vector< std::string > >( { "hello", "world" } ) ==
R"(["hello","world"])" );
CHECK( spb::json::serialize< std::vector< std::string > >( { } ) == "" );
CHECK( spb::json::serialize< std::string, std::vector< std::string > >(
{ "hello", "world" } ) == R"(["hello","world"])" );
CHECK( spb::json::serialize< std::string, std::vector< std::string > >( { } ) ==
"" );
}
}
SUBCASE( "bool" )
Expand All @@ -717,93 +720,110 @@ TEST_CASE( "json" )
CHECK( spb::json::serialize( false ) == "false" );
SUBCASE( "optional" )
{
CHECK( spb::json::serialize< std::optional< bool > >( std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< bool > >( true ) == "true" );
CHECK( spb::json::serialize< std::optional< bool > >( false ) == "false" );
CHECK( spb::json::serialize< std::string, std::optional< bool > >( std::nullopt ) ==
"" );
CHECK( spb::json::serialize< std::string, std::optional< bool > >( true ) ==
"true" );
CHECK( spb::json::serialize< std::string, std::optional< bool > >( false ) ==
"false" );
}
SUBCASE( "repeated" )
{
CHECK( spb::json::serialize< std::vector< bool > >( { true, false } ) ==
R"([true,false])" );
CHECK( spb::json::serialize< std::vector< bool > >( { } ) == "" );
CHECK( spb::json::serialize< std::string, std::vector< bool > >(
{ true, false } ) == R"([true,false])" );
CHECK( spb::json::serialize< std::string, std::vector< bool > >( { } ) == "" );
}
}
SUBCASE( "int" )
{
CHECK( spb::json::serialize( 42 ) == "42" );
SUBCASE( "optional" )
{
CHECK( spb::json::serialize< std::optional< int > >( std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< int > >( 42 ) == "42" );
CHECK( spb::json::serialize< std::string, std::optional< int > >( std::nullopt ) ==
"" );
CHECK( spb::json::serialize< std::string, std::optional< int > >( 42 ) == "42" );
}
SUBCASE( "repeated" )
{
CHECK( spb::json::serialize< std::vector< int > >( { 42 } ) == R"([42])" );
CHECK( spb::json::serialize< std::vector< int > >( { 42, 3 } ) == R"([42,3])" );
CHECK( spb::json::serialize< std::vector< int > >( { } ) == "" );
CHECK( spb::json::serialize< std::string, std::vector< int > >( { 42 } ) ==
R"([42])" );
CHECK( spb::json::serialize< std::string, std::vector< int > >( { 42, 3 } ) ==
R"([42,3])" );
CHECK( spb::json::serialize< std::string, std::vector< int > >( { } ) == "" );
}
}
SUBCASE( "double" )
{
CHECK( spb::json::serialize( 42.0 ) == "42" );
SUBCASE( "optional" )
{
CHECK( spb::json::serialize< std::optional< double > >( std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< double > >( 42.3 ) == "42.3" );
CHECK( spb::json::serialize< std::string, std::optional< double > >(
std::nullopt ) == "" );
CHECK( spb::json::serialize< std::string, std::optional< double > >( 42.3 ) ==
"42.3" );
}
SUBCASE( "repeated" )
{
CHECK( spb::json::serialize< std::vector< double > >( { 42.3 } ) == R"([42.3])" );
CHECK( spb::json::serialize< std::vector< double > >( { 42.3, 3.0 } ) ==
R"([42.3,3])" );
CHECK( spb::json::serialize< std::vector< double > >( { } ) == "" );
CHECK( spb::json::serialize< std::string, std::vector< double > >( { 42.3 } ) ==
R"([42.3])" );
CHECK( spb::json::serialize< std::string, std::vector< double > >(
{ 42.3, 3.0 } ) == R"([42.3,3])" );
CHECK( spb::json::serialize< std::string, std::vector< double > >( { } ) == "" );
}
}
SUBCASE( "bytes" )
{
CHECK( spb::json::serialize( to_bytes( "\x00\x01\x02"sv ) ) == R"("AAEC")" );

CHECK( spb::json::serialize< std::vector< std::byte > >(
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >(
to_bytes( "\x00\x01\x02\x03\x04"sv ) ) == R"("AAECAwQ=")" );
CHECK( spb::json::serialize< std::vector< std::byte > >( to_bytes( "hello"sv ) ) ==
R"("aGVsbG8=")" );
CHECK( spb::json::serialize< std::vector< std::byte > >( { } ) == "" );
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >(
to_bytes( "hello"sv ) ) == R"("aGVsbG8=")" );
CHECK( spb::json::serialize< std::string, std::vector< std::byte > >( { } ) == "" );

SUBCASE( "repeated" )
{
CHECK( spb::json::serialize< std::vector< std::vector< std::byte > > >(
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
std::vector< std::vector< std::byte > >{
to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) == R"(["AAECAwQ="])" );
CHECK( spb::json::serialize< std::vector< std::vector< std::byte > > >(
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
std::vector< std::vector< std::byte > >{
to_bytes( "\x00\x01\x02\x03\x04"sv ), to_bytes( "hello"sv ) } ) ==
R"(["AAECAwQ=","aGVsbG8="])" );
CHECK( spb::json::serialize< std::vector< std::vector< std::byte > > >(
CHECK( spb::json::serialize< std::string, std::vector< std::vector< std::byte > > >(
std::vector< std::vector< std::byte > >{ } ) == "" );
}
SUBCASE( "optional" )
{
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
R"("AAECAwQ=")" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ } ) == "" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::nullopt ) == "" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
R"("AAECAwQ=")" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ } ) == "" );
}
SUBCASE( "fixed" )
{
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::nullopt ) == "" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
R"("AAECAwQ=")" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
CHECK( spb::json::serialize< std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ } ) == "" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::nullopt ) == "" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "\x00\x01\x02\x03\x04"sv ) } ) ==
R"("AAECAwQ=")" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ to_bytes( "hello"sv ) } ) == R"("aGVsbG8=")" );
CHECK(
spb::json::serialize< std::string, std::optional< std::vector< std::byte > > >(
std::vector< std::byte >{ } ) == "" );
}
}
SUBCASE( "variant" )
Expand Down
17 changes: 16 additions & 1 deletion test/pb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ auto pb_serialize_as( const T & value ) -> std::string
template < typename T >
void pb_test( const T & value, std::string_view protobuf )
{
{
auto serialized = spb::pb::serialize< std::vector< std::byte > >( value );
CHECK( serialized.size( ) == protobuf.size( ) );
auto proto = std::string_view( ( char * ) serialized.data( ), serialized.size( ) );
CHECK( proto == protobuf );
}

{
auto serialized = spb::pb::serialize( value );
CHECK( serialized == protobuf );
Expand Down Expand Up @@ -153,6 +160,13 @@ void pb_test( const T & value, std::string_view protobuf )
template < typename T >
void json_test( const T & value, std::string_view json )
{
{
auto serialized = spb::json::serialize< std::vector< std::byte > >( value );
CHECK( serialized.size( ) == json.size( ) );
auto js = std::string_view( ( char * ) serialized.data( ), serialized.size( ) );
CHECK( js == json );
}

{
auto serialized = spb::json::serialize( value );
CHECK( serialized == json );
Expand Down Expand Up @@ -245,7 +259,8 @@ TEST_CASE( "protobuf" )
{
CHECK_THROWS( ( void ) spb::pb::deserialize< Test::Scalar::ReqString >(
"\x0a\x02h\x80"sv ) );
CHECK_THROWS( ( void ) spb::json::serialize< std::string >( "h\x80" ) );
CHECK_THROWS(
( void ) spb::json::serialize< std::string, std::string >( "h\x80" ) );
CHECK_THROWS(
( void ) spb::json::deserialize< std::string >( R"("h\u02w1")"sv ) );
CHECK_THROWS(
Expand Down

0 comments on commit 1b99f73

Please sign in to comment.