src/json.cpp

100.0% Lines (25/25) 100.0% List of functions (12/12) 100.0% Branches (9/9)
json.cpp
f(x) Functions (12)
Line Branch TLA Hits Source Code
1 //
2 // Copyright (c) 2026 Mohammad Nejati
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/burl
8 //
9
10 #include <boost/burl/json.hpp>
11
12 #include <boost/capy/buffers.hpp>
13 #include <boost/json/serializer.hpp>
14 #include <boost/json/stream_parser.hpp>
15
16 #include <cstddef>
17 #include <cstdint>
18 #include <optional>
19 #include <string>
20 #include <system_error>
21 #include <utility>
22
23 namespace boost
24 {
25 namespace burl
26 {
27 namespace
28 {
29
30 class json_body
31 {
32 json::value value_;
33
34 public:
35 5x explicit json_body(json::value value)
36 5x : value_(std::move(value))
37 {
38 5x }
39
40 std::optional<std::string>
41 5x content_type() const
42 {
43 5x return "application/json";
44 }
45
46 std::optional<std::uint64_t>
47 5x content_length() const noexcept
48 {
49 // TODO: determine the length for small JSON values that fit in place.
50 // The serialized size is not known without serializing.
51 5x return std::nullopt;
52 }
53
54 capy::io_task<>
55
1/1
✓ Branch 1 taken 30 times.
30x write(capy::any_buffer_sink& sink) const
56 {
57 json::serializer sr;
58 sr.reset(&value_);
59
60 for(;;)
61 {
62 capy::mutable_buffer arr[2];
63 auto dst = sink.prepare(arr);
64
65 std::size_t n = 0;
66 for(capy::mutable_buffer b : dst)
67 {
68 if(sr.done())
69 break;
70 n += sr.read(static_cast<char*>(b.data()), b.size()).size();
71 }
72
73 if(sr.done())
74 {
75 auto [ec] = co_await sink.commit_eof(n);
76 co_return { ec };
77 }
78
79 if(auto [ec] = co_await sink.commit(n); ec)
80 co_return { ec };
81 }
82 60x }
83 };
84
85 } // namespace
86
87 any_request_body
88 2x tag_invoke(body_from_tag<json::value>, json::value value)
89 {
90
1/1
✓ Branch 4 taken 2 times.
2x return json_body{ std::move(value) };
91 }
92
93 capy::io_task<json::value>
94
1/1
✓ Branch 1 taken 17 times.
17x tag_invoke(body_to_tag<json::value>, response& resp)
95 {
96 auto source = resp.as_buffer_source();
97 json::stream_parser parser;
98
99 for(;;)
100 {
101 capy::const_buffer arr[2];
102 auto [ec, bufs] = co_await source.pull(arr);
103 if(ec)
104 {
105 if(ec == capy::error::eof)
106 {
107 parser.finish(ec);
108 if(ec)
109 co_return { ec, {} };
110 co_return { {}, parser.release() };
111 }
112 co_return { ec, {} };
113 }
114 std::size_t n = 0;
115 for(const auto& buf : bufs)
116 {
117 n += parser.write_some(
118 { static_cast<const char*>(buf.data()), buf.size() }, ec);
119 if(ec)
120 co_return { ec, {} };
121 }
122 source.consume(n);
123 }
124 34x }
125
126 any_request_body
127 1x tag_invoke(body_from_tag<json::object>, json::object value)
128 {
129
1/1
✓ Branch 5 taken 1 time.
1x return json_body{ std::move(value) };
130 }
131
132 capy::io_task<json::object>
133
1/1
✓ Branch 1 taken 2 times.
2x tag_invoke(body_to_tag<json::object>, response& resp)
134 {
135 auto [ec, jv] = co_await tag_invoke(body_to_tag<json::value>{}, resp);
136 if(ec)
137 co_return { ec, {} };
138 auto r = jv.try_as_object();
139 if(r.has_error())
140 co_return { r.error(), {} };
141 co_return { {}, std::move(*r) };
142 4x }
143
144 any_request_body
145 1x tag_invoke(body_from_tag<json::array>, json::array value)
146 {
147
1/1
✓ Branch 5 taken 1 time.
1x return json_body{ std::move(value) };
148 }
149
150 capy::io_task<json::array>
151
1/1
✓ Branch 1 taken 2 times.
2x tag_invoke(body_to_tag<json::array>, response& resp)
152 {
153 auto [ec, jv] = co_await tag_invoke(body_to_tag<json::value>{}, resp);
154 if(ec)
155 co_return { ec, {} };
156 auto r = jv.try_as_array();
157 if(r.has_error())
158 co_return { r.error(), {} };
159 co_return { {}, std::move(*r) };
160 4x }
161
162 any_request_body
163 1x tag_invoke(body_from_tag<json::string>, json::string value)
164 {
165
1/1
✓ Branch 5 taken 1 time.
1x return json_body{ std::move(value) };
166 }
167
168 capy::io_task<json::string>
169
1/1
✓ Branch 1 taken 2 times.
2x tag_invoke(body_to_tag<json::string>, response& resp)
170 {
171 auto [ec, jv] = co_await tag_invoke(body_to_tag<json::value>{}, resp);
172 if(ec)
173 co_return { ec, {} };
174 auto r = jv.try_as_string();
175 if(r.has_error())
176 co_return { r.error(), {} };
177 co_return { {}, std::move(*r) };
178 4x }
179
180 } // namespace burl
181 } // namespace boost
182