Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Metrics
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
cpp-libs
Actor Framework
Commits
5b4905fb
Commit
5b4905fb
authored
Jan 11, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add new serializer for generating JSON output
parent
1ed9a0b4
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
863 additions
and
9 deletions
+863
-9
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+2
-0
libcaf_core/caf/detail/append_hex.hpp
libcaf_core/caf/detail/append_hex.hpp
+5
-9
libcaf_core/caf/json_writer.hpp
libcaf_core/caf/json_writer.hpp
+232
-0
libcaf_core/src/json_writer.cpp
libcaf_core/src/json_writer.cpp
+485
-0
libcaf_core/test/json_writer.cpp
libcaf_core/test/json_writer.cpp
+139
-0
No files found.
libcaf_core/CMakeLists.txt
View file @
5b4905fb
...
@@ -154,6 +154,7 @@ caf_add_component(
...
@@ -154,6 +154,7 @@ caf_add_component(
src/ipv6_address.cpp
src/ipv6_address.cpp
src/ipv6_endpoint.cpp
src/ipv6_endpoint.cpp
src/ipv6_subnet.cpp
src/ipv6_subnet.cpp
src/json_writer.cpp
src/load_inspector.cpp
src/load_inspector.cpp
src/local_actor.cpp
src/local_actor.cpp
src/logger.cpp
src/logger.cpp
...
@@ -290,6 +291,7 @@ caf_add_component(
...
@@ -290,6 +291,7 @@ caf_add_component(
ipv6_address
ipv6_address
ipv6_endpoint
ipv6_endpoint
ipv6_subnet
ipv6_subnet
json_writer
load_inspector
load_inspector
logger
logger
mailbox_element
mailbox_element
...
...
libcaf_core/caf/detail/append_hex.hpp
View file @
5b4905fb
...
@@ -18,24 +18,20 @@ enum class hex_format {
...
@@ -18,24 +18,20 @@ enum class hex_format {
lowercase
,
lowercase
,
};
};
template
<
hex_format
format
=
hex_format
::
uppercase
>
template
<
hex_format
format
=
hex_format
::
uppercase
,
class
Buf
=
std
::
string
>
void
append_hex
(
std
::
string
&
result
,
const
void
*
vptr
,
size_t
n
)
{
void
append_hex
(
Buf
&
result
,
const
void
*
vptr
,
size_t
n
)
{
if
(
n
==
0
)
{
if
(
n
==
0
)
result
+=
"00"
;
return
;
return
;
}
auto
xs
=
reinterpret_cast
<
const
uint8_t
*>
(
vptr
);
auto
xs
=
reinterpret_cast
<
const
uint8_t
*>
(
vptr
);
const
char
*
tbl
;
const
char
*
tbl
;
if
constexpr
(
format
==
hex_format
::
uppercase
)
if
constexpr
(
format
==
hex_format
::
uppercase
)
tbl
=
"0123456789ABCDEF"
;
tbl
=
"0123456789ABCDEF"
;
else
else
tbl
=
"0123456789abcdef"
;
tbl
=
"0123456789abcdef"
;
char
buf
[
3
]
=
{
0
,
0
,
0
};
for
(
size_t
i
=
0
;
i
<
n
;
++
i
)
{
for
(
size_t
i
=
0
;
i
<
n
;
++
i
)
{
auto
c
=
xs
[
i
];
auto
c
=
xs
[
i
];
buf
[
0
]
=
tbl
[
c
>>
4
];
result
.
push_back
(
tbl
[
c
>>
4
]);
buf
[
1
]
=
tbl
[
c
&
0x0F
];
result
.
push_back
(
tbl
[
c
&
0x0F
]);
result
+=
buf
;
}
}
}
}
...
...
libcaf_core/caf/json_writer.hpp
0 → 100644
View file @
5b4905fb
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include <vector>
#include "caf/detail/core_export.hpp"
#include "caf/serializer.hpp"
namespace
caf
{
/// Serializes an inspectable object to a JSON-formatted string.
class
CAF_CORE_EXPORT
json_writer
:
public
serializer
{
public:
// -- member types -----------------------------------------------------------
using
super
=
serializer
;
/// Reflects the structure of JSON objects according to ECMA-404. This enum
/// skips types such as `members` or `value` since they are not needed to
/// generate JSON.
enum
class
type
:
uint8_t
{
element
,
/// Can morph into any other type except `member`.
object
,
/// Contains any number of members.
member
,
/// A single object member (key/value pair).
array
,
/// Contains any number of elements.
string
,
/// A character sequence (terminal type).
number
,
/// An integer or floating point (terminal type).
boolean
,
/// Either "true" or "false" (terminal type).
null
,
/// The literal "null" (terminal type).
};
// -- constructors, destructors, and assignment operators --------------------
json_writer
();
explicit
json_writer
(
actor_system
&
sys
);
explicit
json_writer
(
execution_unit
*
ctx
);
~
json_writer
()
override
;
// -- properties -------------------------------------------------------------
/// Returns a string view into the internal buffer.
/// @warning This view becomes invalid when calling any non-const member
/// function on the writer object.
[[
nodiscard
]]
string_view
str
()
const
noexcept
{
return
{
buf_
.
data
(),
buf_
.
size
()};
}
/// Returns the current indentation factor.
[[
nodiscard
]]
size_t
indentation
()
const
noexcept
{
return
indentation_factor_
;
}
/// Sets the indentation level.
/// @param factor The number of spaces to add to each level of indentation. A
/// value of 0 (the default) disables indentation, printing the
/// entire JSON output into a single line.
void
indentation
(
size_t
factor
)
noexcept
{
indentation_factor_
=
factor
;
}
/// Returns whether the writer generates compact JSON output without any
/// spaces or newlines to separate values.
[[
nodiscard
]]
bool
compact
()
const
noexcept
{
return
indentation_factor_
==
0
;
}
// -- modifiers --------------------------------------------------------------
/// Removes all characters from the buffer and restores the writer to its
/// initial state.
/// @warning Invalidates all string views into the buffer.
void
reset
();
// -- overrides --------------------------------------------------------------
bool
begin_object
(
type_id_t
type
,
string_view
name
)
override
;
bool
end_object
()
override
;
bool
begin_field
(
string_view
)
override
;
bool
begin_field
(
string_view
name
,
bool
is_present
)
override
;
bool
begin_field
(
string_view
name
,
span
<
const
type_id_t
>
types
,
size_t
index
)
override
;
bool
begin_field
(
string_view
name
,
bool
is_present
,
span
<
const
type_id_t
>
types
,
size_t
index
)
override
;
bool
end_field
()
override
;
bool
begin_tuple
(
size_t
size
)
override
;
bool
end_tuple
()
override
;
bool
begin_key_value_pair
()
override
;
bool
end_key_value_pair
()
override
;
bool
begin_sequence
(
size_t
size
)
override
;
bool
end_sequence
()
override
;
bool
begin_associative_array
(
size_t
size
)
override
;
bool
end_associative_array
()
override
;
bool
value
(
byte
x
)
override
;
bool
value
(
bool
x
)
override
;
bool
value
(
int8_t
x
)
override
;
bool
value
(
uint8_t
x
)
override
;
bool
value
(
int16_t
x
)
override
;
bool
value
(
uint16_t
x
)
override
;
bool
value
(
int32_t
x
)
override
;
bool
value
(
uint32_t
x
)
override
;
bool
value
(
int64_t
x
)
override
;
bool
value
(
uint64_t
x
)
override
;
bool
value
(
float
x
)
override
;
bool
value
(
double
x
)
override
;
bool
value
(
long
double
x
)
override
;
bool
value
(
string_view
x
)
override
;
bool
value
(
const
std
::
u16string
&
x
)
override
;
bool
value
(
const
std
::
u32string
&
x
)
override
;
bool
value
(
span
<
const
byte
>
x
)
override
;
private:
// -- implementation details -------------------------------------------------
template
<
class
T
>
bool
number
(
T
);
// -- state management -------------------------------------------------------
void
init
();
// Returns the current top of the stack or `null_literal` if empty.
type
top
();
// Enters a new level of nesting.
void
push
(
type
=
type
::
element
);
// Enters a new level of nesting if the current top is `expected`.
bool
push_if
(
type
expected
,
type
=
type
::
element
);
// Backs up one level of nesting.
bool
pop
();
// Backs up one level of nesting but checks that current top is `t` before.
bool
pop_if
(
type
t
);
// Backs up one level of nesting but checks that the top is `t` afterwards.
bool
pop_if_next
(
type
t
);
// Tries to morph the current top of the stack to t.
bool
morph
(
type
t
);
// Tries to morph the current top of the stack to t. Stores the previous value
// to `prev`.
bool
morph
(
type
t
,
type
&
prev
);
// Morphs the current top of the stack to t without performing *any* checks.
void
unsafe_morph
(
type
t
);
// Sets an error reason that the inspector failed to write a t.
void
fail
(
type
t
);
// -- printing ---------------------------------------------------------------
// Adds a newline unless `compact() == true`.
void
nl
();
// Adds `c` to the output buffer.
void
add
(
char
c
)
{
buf_
.
push_back
(
c
);
}
// Adds `str` to the output buffer.
void
add
(
string_view
str
)
{
buf_
.
insert
(
buf_
.
end
(),
str
.
begin
(),
str
.
end
());
}
// Adds a separator to the output buffer unless the current entry is empty.
// The separator is just a comma when in compact mode and otherwise a comma
// followed by a newline.
void
sep
();
// -- member variables -------------------------------------------------------
// The current level of indentation.
size_t
indentation_level_
=
0
;
// The number of whitespaces to add per indentation level.
size_t
indentation_factor_
=
0
;
// Buffer for producing the JSON output.
std
::
vector
<
char
>
buf_
;
struct
entry
{
type
t
;
bool
filled
;
friend
bool
operator
==
(
entry
x
,
type
y
)
noexcept
{
return
x
.
t
==
y
;
};
};
// Bookkeeping for where we are in the current object.
std
::
vector
<
entry
>
stack_
;
};
}
// namespace caf
libcaf_core/src/json_writer.cpp
0 → 100644
View file @
5b4905fb
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/json_writer.hpp"
#include "caf/detail/append_hex.hpp"
#include "caf/detail/print.hpp"
namespace
caf
{
namespace
{
constexpr
bool
can_morph
(
json_writer
::
type
from
,
json_writer
::
type
to
)
{
return
from
==
json_writer
::
type
::
element
&&
to
!=
json_writer
::
type
::
member
;
}
constexpr
const
char
*
json_type_names
[]
=
{
"element"
,
"object"
,
"member"
,
"array"
,
"string"
,
"number"
,
"bool"
,
"null"
};
constexpr
const
char
*
json_type_name
(
json_writer
::
type
t
)
{
return
json_type_names
[
static_cast
<
uint8_t
>
(
t
)];
}
}
// namespace
// -- implementation details ---------------------------------------------------
template
<
class
T
>
bool
json_writer
::
number
(
T
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
number
);
detail
::
print
(
buf_
,
x
);
return
true
;
case
type
:
:
member
:
unsafe_morph
(
type
::
element
);
add
(
'"'
);
detail
::
print
(
buf_
,
x
);
add
(
"
\"
: "
);
return
true
;
case
type
:
:
array
:
sep
();
detail
::
print
(
buf_
,
x
);
return
true
;
default:
fail
(
type
::
number
);
return
false
;
}
}
// -- constructors, destructors, and assignment operators ----------------------
json_writer
::
json_writer
()
:
json_writer
(
nullptr
)
{
init
();
}
json_writer
::
json_writer
(
actor_system
&
sys
)
:
super
(
sys
)
{
init
();
}
json_writer
::
json_writer
(
execution_unit
*
ctx
)
:
super
(
ctx
)
{
init
();
}
json_writer
::~
json_writer
()
{
// nop
}
// -- modifiers ----------------------------------------------------------------
void
json_writer
::
reset
()
{
buf_
.
clear
();
stack_
.
clear
();
push
();
}
// -- overrides ----------------------------------------------------------------
bool
json_writer
::
begin_object
(
type_id_t
,
string_view
name
)
{
auto
add_type_annotation
=
[
this
,
name
]
{
add
(
R"_("@type": )_"
);
detail
::
print_escaped
(
buf_
,
name
);
return
true
;
};
return
begin_associative_array
(
0
)
// Put opening paren, ...
&&
begin_key_value_pair
()
// ... add implicit @type member, ..
&&
add_type_annotation
()
// ... write content ...
&&
end_key_value_pair
();
// ... and wait for next field.
}
bool
json_writer
::
end_object
()
{
return
end_associative_array
();
}
bool
json_writer
::
begin_field
(
string_view
name
)
{
if
(
begin_key_value_pair
())
{
detail
::
print_escaped
(
buf_
,
name
);
add
(
": "
);
unsafe_morph
(
type
::
element
);
return
true
;
}
else
{
return
false
;
}
}
bool
json_writer
::
begin_field
(
string_view
name
,
bool
is_present
)
{
if
(
begin_key_value_pair
())
{
detail
::
print_escaped
(
buf_
,
name
);
add
(
": "
);
if
(
is_present
)
unsafe_morph
(
type
::
element
);
else
add
(
"null"
);
return
true
;
}
else
{
return
false
;
}
}
bool
json_writer
::
begin_field
(
string_view
name
,
span
<
const
type_id_t
>
,
size_t
)
{
return
begin_field
(
name
);
}
bool
json_writer
::
begin_field
(
string_view
name
,
bool
is_present
,
span
<
const
type_id_t
>
,
size_t
)
{
return
begin_field
(
name
,
is_present
);
}
bool
json_writer
::
end_field
()
{
return
pop_if_next
(
type
::
object
);
}
bool
json_writer
::
begin_tuple
(
size_t
size
)
{
return
begin_sequence
(
size
);
}
bool
json_writer
::
end_tuple
()
{
return
end_sequence
();
}
bool
json_writer
::
begin_key_value_pair
()
{
sep
();
return
push_if
(
type
::
object
,
type
::
member
);
}
bool
json_writer
::
end_key_value_pair
()
{
// Note: can't check for `type::member` here, because it has been morphed to
// whatever the value type has been. But after popping the current type, there
// still has to be the `object` entry below.
return
pop_if_next
(
type
::
object
);
}
bool
json_writer
::
begin_sequence
(
size_t
)
{
switch
(
top
())
{
default:
emplace_error
(
sec
::
runtime_error
,
"unexpected begin_sequence"
);
return
false
;
case
type
:
:
element
:
unsafe_morph
(
type
::
array
);
break
;
case
type
:
:
array
:
push
(
type
::
array
);
break
;
}
add
(
'['
);
++
indentation_level_
;
nl
();
return
true
;
}
bool
json_writer
::
end_sequence
()
{
if
(
pop_if
(
type
::
array
))
{
--
indentation_level_
;
nl
();
add
(
']'
);
return
true
;
}
else
{
return
false
;
}
}
bool
json_writer
::
begin_associative_array
(
size_t
)
{
switch
(
top
())
{
default:
emplace_error
(
sec
::
runtime_error
,
"unexpected begin_object or begin_associative_array"
);
return
false
;
case
type
:
:
member
:
emplace_error
(
sec
::
runtime_error
,
"unimplemented: json_writer currently does not support "
"complex types as dictionary keys"
);
return
false
;
case
type
:
:
element
:
unsafe_morph
(
type
::
object
);
break
;
case
type
:
:
array
:
push
(
type
::
object
);
break
;
}
add
(
'{'
);
++
indentation_level_
;
nl
();
return
true
;
}
bool
json_writer
::
end_associative_array
()
{
if
(
pop_if
(
type
::
object
))
{
--
indentation_level_
;
nl
();
add
(
'}'
);
if
(
top
()
==
type
::
array
)
sep
();
return
true
;
}
else
{
return
false
;
}
}
bool
json_writer
::
value
(
byte
x
)
{
return
number
(
to_integer
<
uint8_t
>
(
x
));
}
bool
json_writer
::
value
(
bool
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
int8_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
uint8_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
int16_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
uint16_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
int32_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
uint32_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
int64_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
uint64_t
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
float
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
double
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
long
double
x
)
{
return
number
(
x
);
}
bool
json_writer
::
value
(
string_view
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
string
);
detail
::
print_escaped
(
buf_
,
x
);
return
true
;
case
type
:
:
member
:
unsafe_morph
(
type
::
element
);
detail
::
print_escaped
(
buf_
,
x
);
add
(
": "
);
return
true
;
case
type
:
:
array
:
sep
();
detail
::
print_escaped
(
buf_
,
x
);
return
true
;
default:
fail
(
type
::
string
);
return
false
;
}
}
bool
json_writer
::
value
(
const
std
::
u16string
&
)
{
emplace_error
(
sec
::
unsupported_operation
,
"u16string not supported yet by caf::json_writer"
);
return
false
;
}
bool
json_writer
::
value
(
const
std
::
u32string
&
)
{
emplace_error
(
sec
::
unsupported_operation
,
"u32string not supported yet by caf::json_writer"
);
return
false
;
}
bool
json_writer
::
value
(
span
<
const
byte
>
x
)
{
switch
(
top
())
{
case
type
:
:
element
:
unsafe_morph
(
type
::
string
);
add
(
'"'
);
detail
::
append_hex
(
buf_
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
add
(
'"'
);
return
true
;
case
type
:
:
member
:
unsafe_morph
(
type
::
element
);
add
(
'"'
);
detail
::
append_hex
(
buf_
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
add
(
"
\"
: "
);
return
true
;
case
type
:
:
array
:
sep
();
add
(
'"'
);
detail
::
append_hex
(
buf_
,
reinterpret_cast
<
const
void
*>
(
x
.
data
()),
x
.
size
());
add
(
'"'
);
return
true
;
default:
fail
(
type
::
string
);
return
false
;
}
}
// -- state management ---------------------------------------------------------
void
json_writer
::
init
()
{
has_human_readable_format_
=
true
;
// Reserve some reasonable storage for the character buffer. JSON grows
// quickly, so we can start at 1kb to avoid a couple of small allocations in
// the beginning.
buf_
.
reserve
(
1024
);
// Even heavily nested objects should fit into 32 levels of nesting.
stack_
.
reserve
(
32
);
// The concrete type of the output is yet unknown.
push
();
}
json_writer
::
type
json_writer
::
top
()
{
if
(
!
stack_
.
empty
())
return
stack_
.
back
().
t
;
else
return
type
::
null
;
}
// Enters a new level of nesting.
void
json_writer
::
push
(
type
t
)
{
stack_
.
push_back
({
t
,
false
});
}
bool
json_writer
::
push_if
(
type
expected
,
type
t
)
{
if
(
!
stack_
.
empty
()
&&
stack_
.
back
()
==
expected
)
{
stack_
.
push_back
({
t
,
false
});
return
true
;
}
else
{
std
::
string
str
=
"push_if failed: expected = "
;
str
+=
json_type_name
(
expected
);
str
+=
", found = "
;
str
+=
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
// Backs up one level of nesting.
bool
json_writer
::
pop
()
{
if
(
!
stack_
.
empty
())
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop() called with an empty stack: begin/end mismatch"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
bool
json_writer
::
pop_if
(
type
t
)
{
if
(
!
stack_
.
empty
()
&&
stack_
.
back
()
==
t
)
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop_if failed: expected = "
;
str
+=
json_type_name
(
t
);
if
(
stack_
.
empty
())
{
str
+=
", found an empty stack"
;
}
else
{
str
+=
", found = "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
bool
json_writer
::
pop_if_next
(
type
t
)
{
if
(
stack_
.
size
()
>
1
&&
stack_
[
stack_
.
size
()
-
2
]
==
t
)
{
stack_
.
pop_back
();
return
true
;
}
else
{
std
::
string
str
=
"pop_if_next failed: expected = "
;
str
+=
json_type_name
(
t
);
if
(
stack_
.
size
()
<
2
)
{
str
+=
", found a stack of size "
;
detail
::
print
(
str
,
stack_
.
size
());
}
else
{
str
+=
", found = "
;
str
+=
json_type_name
(
stack_
[
stack_
.
size
()
-
2
].
t
);
}
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
// Tries to morph the current top of the stack to t.
bool
json_writer
::
morph
(
type
t
)
{
type
unused
;
return
morph
(
t
,
unused
);
}
bool
json_writer
::
morph
(
type
t
,
type
&
prev
)
{
if
(
!
stack_
.
empty
())
{
if
(
can_morph
(
stack_
.
back
().
t
,
t
))
{
prev
=
stack_
.
back
().
t
;
stack_
.
back
().
t
=
t
;
return
true
;
}
else
{
std
::
string
str
=
"cannot convert "
;
str
+=
json_type_name
(
stack_
.
back
().
t
);
str
+=
" to "
;
str
+=
json_type_name
(
t
);
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
else
{
std
::
string
str
=
"mismatched begin/end calls on the JSON inspector"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
return
false
;
}
}
void
json_writer
::
unsafe_morph
(
type
t
)
{
stack_
.
back
().
t
=
t
;
}
void
json_writer
::
fail
(
type
t
)
{
std
::
string
str
=
"failed to write a "
;
str
+=
json_type_name
(
t
);
str
+=
": invalid position (begin/end mismatch?)"
;
emplace_error
(
sec
::
runtime_error
,
std
::
move
(
str
));
}
// -- printing ---------------------------------------------------------------
void
json_writer
::
nl
()
{
if
(
indentation_factor_
>
0
)
{
buf_
.
push_back
(
'\n'
);
buf_
.
insert
(
buf_
.
end
(),
indentation_factor_
*
indentation_level_
,
' '
);
}
}
void
json_writer
::
sep
()
{
CAF_ASSERT
(
top
()
==
type
::
object
||
top
()
==
type
::
array
);
if
(
stack_
.
back
().
filled
)
{
if
(
indentation_factor_
>
0
)
{
add
(
",
\n
"
);
buf_
.
insert
(
buf_
.
end
(),
indentation_factor_
*
indentation_level_
,
' '
);
}
else
{
add
(
", "
);
}
}
else
{
stack_
.
back
().
filled
=
true
;
}
}
}
// namespace caf
libcaf_core/test/json_writer.cpp
0 → 100644
View file @
5b4905fb
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE json_writer
#include "caf/json_writer.hpp"
#include "core-test.hpp"
using
namespace
caf
;
using
namespace
std
::
literals
::
string_literals
;
namespace
{
struct
fixture
{
template
<
class
T
>
expected
<
std
::
string
>
to_json_string
(
T
&&
x
,
size_t
indentation_factor
)
{
json_writer
writer
;
writer
.
indentation
(
indentation_factor
);
if
(
writer
.
apply
(
std
::
forward
<
T
>
(
x
)))
{
auto
buf
=
writer
.
str
();
return
{
std
::
string
{
buf
.
begin
(),
buf
.
end
()}};
}
else
{
MESSAGE
(
"partial JSON output: "
<<
writer
.
str
());
return
{
writer
.
get_error
()};
}
}
};
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
json_writer_tests
,
fixture
)
SCENARIO
(
"the JSON writer converts builtin types to strings"
)
{
GIVEN
(
"an integer"
)
{
auto
x
=
42
;
WHEN
(
"converting it to JSON with any indentation factor"
)
{
THEN
(
"the JSON output is the number"
)
{
CHECK_EQ
(
to_json_string
(
x
,
0
),
"42"
s
);
CHECK_EQ
(
to_json_string
(
x
,
2
),
"42"
s
);
}
}
}
GIVEN
(
"a string"
)
{
auto
x
=
R"_(hello "world"!)_"
s
;
WHEN
(
"converting it to JSON with any indentation factor"
)
{
THEN
(
"the JSON output is the escaped string"
)
{
CHECK_EQ
(
to_json_string
(
x
,
0
),
R"_("hello \"world\"!")_"
s
);
CHECK_EQ
(
to_json_string
(
x
,
2
),
R"_("hello \"world\"!")_"
s
);
}
}
}
GIVEN
(
"a list"
)
{
auto
x
=
std
::
vector
<
int
>
{
1
,
2
,
3
};
WHEN
(
"converting it to JSON with indentation factor 0"
)
{
THEN
(
"the JSON output is a single line"
)
{
CHECK_EQ
(
to_json_string
(
x
,
0
),
"[1, 2, 3]"
s
);
}
}
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON output uses multiple lines"
)
{
auto
out
=
R"_([
1,
2,
3
])_"
s
;
CHECK_EQ
(
to_json_string
(
x
,
2
),
out
);
}
}
}
GIVEN
(
"a dictionary"
)
{
std
::
map
<
std
::
string
,
std
::
string
>
x
;
x
.
emplace
(
"a"
,
"A"
);
x
.
emplace
(
"b"
,
"B"
);
x
.
emplace
(
"c"
,
"C"
);
WHEN
(
"converting it to JSON with indentation factor 0"
)
{
THEN
(
"the JSON output is a single line"
)
{
CHECK_EQ
(
to_json_string
(
x
,
0
),
R"_({"a": "A", "b": "B", "c": "C"})_"
s
);
}
}
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON output uses multiple lines"
)
{
auto
out
=
R"_({
"a": "A",
"b": "B",
"c": "C"
})_"
s
;
CHECK_EQ
(
to_json_string
(
x
,
2
),
out
);
}
}
}
GIVEN
(
"a message"
)
{
auto
x
=
make_message
(
put_atom_v
,
"foo"
,
42
);
WHEN
(
"converting it to JSON with indentation factor 0"
)
{
THEN
(
"the JSON output is a single line"
)
{
CHECK_EQ
(
to_json_string
(
x
,
0
),
R"_([{"@type": "caf::put_atom"}, "foo", 42])_"
s
);
}
}
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON output uses multiple lines"
)
{
auto
out
=
R"_([
{
"@type": "caf::put_atom"
},
"foo",
42
])_"
s
;
CHECK_EQ
(
to_json_string
(
x
,
2
),
out
);
}
}
}
}
SCENARIO
(
"the JSON writer converts simple structs to strings"
)
{
GIVEN
(
"a dummy_struct object"
)
{
dummy_struct
x
{
10
,
"foo"
};
WHEN
(
"converting it to JSON with indentation factor 0"
)
{
THEN
(
"the JSON output is a single line"
)
{
auto
out
=
R"_({"@type": "dummy_struct", "a": 10, "b": "foo"})_"
s
;
CHECK_EQ
(
to_json_string
(
x
,
0
),
out
);
}
}
WHEN
(
"converting it to JSON with indentation factor 2"
)
{
THEN
(
"the JSON output uses multiple lines"
)
{
auto
out
=
R"_({
"@type": "dummy_struct",
"a": 10,
"b": "foo"
})_"
s
;
CHECK_EQ
(
to_json_string
(
x
,
2
),
out
);
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment