libs/http/include/boost/http/json/json_sink.hpp

97.1% Lines (34/35) 100.0% Functions (8/8) 87.5% Branches (14/16)
libs/http/include/boost/http/json/json_sink.hpp
Line Branch Hits Source Code
1 //
2 // Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
3 //
4 // Distributed under the Boost Software License, Version 1.0. (See accompanying
5 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // Official repository: https://github.com/cppalliance/http
8 //
9
10 #ifndef BOOST_HTTP_JSON_JSON_SINK_HPP
11 #define BOOST_HTTP_JSON_JSON_SINK_HPP
12
13 #include <boost/http/config.hpp>
14
15 #include <boost/capy/buffers.hpp>
16 #include <boost/capy/concept/const_buffer_sequence.hpp>
17 #include <boost/capy/ex/immediate.hpp>
18 #include <boost/capy/io_result.hpp>
19 #include <boost/json/stream_parser.hpp>
20 #include <boost/json/value.hpp>
21
22 namespace boost {
23 namespace http {
24
25 /** A sink for streaming JSON data to a parser.
26
27 This class wraps a `boost::json::stream_parser` and satisfies the
28 @ref capy::WriteSink concept, enabling incremental JSON parsing
29 from any data source that produces buffer sequences.
30
31 Since JSON parsing is synchronous, all operations return
32 @ref capy::immediate awaitables with zero suspension overhead.
33
34 @par Example
35 @code
36 json_sink sink;
37
38 // Write JSON data incrementally
39 auto [ec1, n1] = co_await sink.write(capy::make_buffer("{\"key\":"));
40 auto [ec2, n2] = co_await sink.write(capy::make_buffer("42}"), true);
41
42 // Or use write_eof() separately
43 auto [ec3] = co_await sink.write_eof();
44
45 // Retrieve the parsed value
46 json::value v = sink.release();
47 @endcode
48
49 @par Thread Safety
50 Distinct objects: Safe.
51 Shared objects: Unsafe.
52
53 @see capy::WriteSink, json::stream_parser
54 */
55 class json_sink
56 {
57 json::stream_parser parser_;
58
59 public:
60 /** Default constructor.
61
62 Constructs a sink with a default-initialized stream parser.
63 */
64 8 json_sink() = default;
65
66 /** Constructor with parse options.
67
68 @param opt Options controlling JSON parsing behavior.
69 */
70 explicit
71 1 json_sink(json::parse_options const& opt)
72 1 : parser_(json::storage_ptr(), opt)
73 {
74 1 }
75
76 /** Constructor with storage and parse options.
77
78 @param sp The storage to use for parsed values.
79 @param opt Options controlling JSON parsing behavior.
80 */
81 json_sink(
82 json::storage_ptr sp,
83 json::parse_options const& opt = {})
84 : parser_(std::move(sp), opt)
85 {
86 }
87
88 /** Write data to the JSON parser.
89
90 Writes all bytes from the buffer sequence to the stream parser.
91
92 @param buffers Buffer sequence containing JSON data.
93
94 @return An awaitable yielding `(error_code,std::size_t)`.
95 On success, returns the total bytes written.
96 */
97 template<capy::ConstBufferSequence CB>
98 capy::immediate<capy::io_result<std::size_t>>
99 5 write(CB const& buffers)
100 {
101 5 return write(buffers, false);
102 }
103
104 /** Write data with optional end-of-stream.
105
106 Writes all bytes from the buffer sequence to the stream parser.
107 If @p eof is true, also finishes parsing.
108
109 @param buffers Buffer sequence containing JSON data.
110 @param eof If true, signals end of JSON data after writing.
111
112 @return An awaitable yielding `(error_code,std::size_t)`.
113 On success, returns the total bytes written.
114 */
115 template<capy::ConstBufferSequence CB>
116 capy::immediate<capy::io_result<std::size_t>>
117 12 write(CB const& buffers, bool eof)
118 {
119 12 system::error_code ec;
120 12 std::size_t total = 0;
121 12 auto const end = capy::end(buffers);
122
2/2
✓ Branch 1 taken 12 times.
✓ Branch 2 taken 11 times.
23 for(auto it = capy::begin(buffers); it != end; ++it)
123 {
124 12 capy::const_buffer buf(*it);
125
1/1
✓ Branch 2 taken 12 times.
24 auto n = parser_.write(
126 12 static_cast<char const*>(buf.data()),
127 buf.size(),
128 ec);
129 12 total += n;
130
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 11 times.
12 if(ec.failed())
131
1/1
✓ Branch 1 taken 1 time.
1 return {ec, total};
132 }
133
134
2/2
✓ Branch 0 taken 6 times.
✓ Branch 1 taken 5 times.
11 if(eof)
135 {
136
1/1
✓ Branch 1 taken 6 times.
6 parser_.finish(ec);
137
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 6 times.
6 if(ec.failed())
138 return {ec, total};
139 }
140
141 11 return capy::ready(total);
142 }
143
144 /** Signal end of JSON data.
145
146 Finishes parsing and validates the JSON is complete.
147
148 @return An awaitable yielding `(error_code)`.
149 */
150 capy::immediate<capy::io_result<>>
151 2 write_eof()
152 {
153 2 system::error_code ec;
154
1/1
✓ Branch 1 taken 2 times.
2 parser_.finish(ec);
155
2/2
✓ Branch 1 taken 1 time.
✓ Branch 2 taken 1 time.
2 if(ec.failed())
156
1/1
✓ Branch 1 taken 1 time.
1 return {ec};
157 1 return {};
158 }
159
160 /** Check if parsing is complete.
161
162 @return `true` if a complete JSON value has been parsed.
163 */
164 bool
165 11 done() const noexcept
166 {
167 11 return parser_.done();
168 }
169
170 /** Release the parsed JSON value.
171
172 Returns the parsed value and resets the parser for reuse.
173
174 @par Preconditions
175 `this->done() == true`
176
177 @return The parsed JSON value.
178 */
179 json::value
180 7 release()
181 {
182 7 return parser_.release();
183 }
184
185 /** Reset the parser for a new JSON value.
186
187 Clears all state and prepares to parse a new value.
188 */
189 void
190 1 reset()
191 {
192 1 parser_.reset();
193 1 }
194 };
195
196 } // namespace http
197 } // namespace boost
198
199 #endif
200