diff options
| author | Rutger Broekhoff | 2026-08-28 22:22:24 +0200 |
|---|---|---|
| committer | Rutger Broekhoff | 2026-08-28 22:22:24 +0200 |
| commit | b0d7600970d2fdb1eb6fff813c7336ea34d4e228 (patch) | |
| tree | 051d6f80c7e332f4b31027025ae7c60c7ac70742 /server/src | |
| parent | 70643a9e4821bd11c3f363f1708fe420965467d1 (diff) | |
| download | routemon-b0d7600970d2fdb1eb6fff813c7336ea34d4e228.tar.gz routemon-b0d7600970d2fdb1eb6fff813c7336ea34d4e228.zip | |
Indent tweaks
Diffstat (limited to 'server/src')
| -rw-r--r-- | server/src/api.cpp | 126 | ||||
| -rw-r--r-- | server/src/api.cppm | 24 | ||||
| -rw-r--r-- | server/src/config.cpp | 163 | ||||
| -rw-r--r-- | server/src/database.cppm | 8 | ||||
| -rw-r--r-- | server/src/datex2.cppm | 112 | ||||
| -rw-r--r-- | server/src/geo.cppm | 34 | ||||
| -rw-r--r-- | server/src/gpx.cpp | 62 | ||||
| -rw-r--r-- | server/src/http_client.cppm | 6 | ||||
| -rw-r--r-- | server/src/http_server.cppm | 322 | ||||
| -rw-r--r-- | server/src/locale.cppm | 33 | ||||
| -rw-r--r-- | server/src/log.cppm | 14 | ||||
| -rw-r--r-- | server/src/main.cpp | 28 | ||||
| -rw-r--r-- | server/src/problem.cppm | 4 | ||||
| -rw-r--r-- | server/src/req_ctx.cppm | 8 | ||||
| -rw-r--r-- | server/src/rwgps.cppm | 20 | ||||
| -rw-r--r-- | server/src/sqlite3.cppm | 82 | ||||
| -rw-r--r-- | server/src/srv.cppm | 58 | ||||
| -rw-r--r-- | server/src/time.cppm | 2 | ||||
| -rw-r--r-- | server/src/trace.cppm | 14 | ||||
| -rw-r--r-- | server/src/util.cppm | 25 | ||||
| -rw-r--r-- | server/src/xml.cpp | 6 | ||||
| -rw-r--r-- | server/src/xml.cppm | 140 |
22 files changed, 656 insertions, 635 deletions
diff --git a/server/src/api.cpp b/server/src/api.cpp index 73dee32..9317f1b 100644 --- a/server/src/api.cpp +++ b/server/src/api.cpp | |||
| @@ -39,7 +39,7 @@ auto json_value_from_linestring(geo::linestring const& ls) -> json::value | |||
| 39 | } | 39 | } |
| 40 | 40 | ||
| 41 | auto json_value_from_linestrings(std::vector<geo::linestring> const& lss) | 41 | auto json_value_from_linestrings(std::vector<geo::linestring> const& lss) |
| 42 | -> json::value | 42 | -> json::value |
| 43 | { | 43 | { |
| 44 | json::array a; | 44 | json::array a; |
| 45 | for (auto const& ls : lss) | 45 | for (auto const& ls : lss) |
| @@ -48,8 +48,8 @@ auto json_value_from_linestrings(std::vector<geo::linestring> const& lss) | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | auto tag_invoke( | 50 | auto tag_invoke( |
| 51 | json::value_from_tag, json::value& jv, relevant_road_closure const& clo) | 51 | json::value_from_tag, json::value& jv, relevant_road_closure const& clo) |
| 52 | -> void | 52 | -> void |
| 53 | { | 53 | { |
| 54 | jv = json::object{ | 54 | jv = json::object{ |
| 55 | {"relevant_lss", json_value_from_linestrings(clo.relevant_lss)}, | 55 | {"relevant_lss", json_value_from_linestrings(clo.relevant_lss)}, |
| @@ -57,7 +57,8 @@ auto tag_invoke( | |||
| 57 | } | 57 | } |
| 58 | 58 | ||
| 59 | auto tag_invoke( | 59 | auto tag_invoke( |
| 60 | json::value_from_tag, json::value& jv, relevant_situation const& sit) -> void | 60 | json::value_from_tag, json::value& jv, relevant_situation const& sit) |
| 61 | -> void | ||
| 61 | { | 62 | { |
| 62 | jv = json::object{ | 63 | jv = json::object{ |
| 63 | {"id", json::value_from(sit.id)}, | 64 | {"id", json::value_from(sit.id)}, |
| @@ -68,7 +69,7 @@ auto tag_invoke( | |||
| 68 | } | 69 | } |
| 69 | 70 | ||
| 70 | auto tag_invoke(json::value_from_tag, json::value& jv, track_segment const& seg) | 71 | auto tag_invoke(json::value_from_tag, json::value& jv, track_segment const& seg) |
| 71 | -> void | 72 | -> void |
| 72 | { | 73 | { |
| 73 | jv = json::object{ | 74 | jv = json::object{ |
| 74 | {"points", json_value_from_linestring(seg.points)}, | 75 | {"points", json_value_from_linestring(seg.points)}, |
| @@ -76,7 +77,7 @@ auto tag_invoke(json::value_from_tag, json::value& jv, track_segment const& seg) | |||
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | auto tag_invoke(json::value_from_tag, json::value& jv, track const& track) | 79 | auto tag_invoke(json::value_from_tag, json::value& jv, track const& track) |
| 79 | -> void | 80 | -> void |
| 80 | { | 81 | { |
| 81 | jv = json::object{ | 82 | jv = json::object{ |
| 82 | {"segments", json::value_from(track.segments)}, | 83 | {"segments", json::value_from(track.segments)}, |
| @@ -84,7 +85,8 @@ auto tag_invoke(json::value_from_tag, json::value& jv, track const& track) | |||
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | auto tag_invoke( | 87 | auto tag_invoke( |
| 87 | json::value_from_tag, json::value& jv, process_gpx_result const& res) -> void | 88 | json::value_from_tag, json::value& jv, process_gpx_result const& res) |
| 89 | -> void | ||
| 88 | { | 90 | { |
| 89 | jv = json::object{ | 91 | jv = json::object{ |
| 90 | {"tracks", json::value_from(res.tracks)}, | 92 | {"tracks", json::value_from(res.tracks)}, |
| @@ -93,7 +95,7 @@ auto tag_invoke( | |||
| 93 | } | 95 | } |
| 94 | 96 | ||
| 95 | auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) | 97 | auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) |
| 96 | -> void | 98 | -> void |
| 97 | { | 99 | { |
| 98 | jv = json::object{ | 100 | jv = json::object{ |
| 99 | {"using_publication_of", | 101 | {"using_publication_of", |
| @@ -124,14 +126,14 @@ handler::handler(log::logger const& l, datex2::situation_publication pub) | |||
| 124 | } | 126 | } |
| 125 | auto const after_build = chrono::steady_clock::now(); | 127 | auto const after_build = chrono::steady_clock::now(); |
| 126 | auto const dur_build = | 128 | auto const dur_build = |
| 127 | chrono::duration_cast<chrono::milliseconds>(after_build - before_build); | 129 | chrono::duration_cast<chrono::milliseconds>(after_build - before_build); |
| 128 | l_.info("Indices built in {}", dur_build); | 130 | l_.info("Indices built in {}", dur_build); |
| 129 | l_.info("LSE index size: {}", lse_index_.size()); | 131 | l_.info("LSE index size: {}", lse_index_.size()); |
| 130 | l_.info("Point index size: {}", p_index_.size()); | 132 | l_.info("Point index size: {}", p_index_.size()); |
| 131 | } | 133 | } |
| 132 | 134 | ||
| 133 | auto handler::process_gpx(gpx::file&& gpx_file) | 135 | auto handler::process_gpx(gpx::file&& gpx_file) |
| 134 | -> std::optional<process_gpx_result> | 136 | -> std::optional<process_gpx_result> |
| 135 | { | 137 | { |
| 136 | auto const now = chrono::utc_clock::now(); | 138 | auto const now = chrono::utc_clock::now(); |
| 137 | auto const relevant = std::initializer_list<time::period>{ | 139 | auto const relevant = std::initializer_list<time::period>{ |
| @@ -143,14 +145,14 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 143 | for (auto const& track : gpx_file.tracks) | 145 | for (auto const& track : gpx_file.tracks) |
| 144 | for (auto const& seg : track.segments) | 146 | for (auto const& seg : track.segments) |
| 145 | geo::split_linestring_with_overlap_segments( | 147 | geo::split_linestring_with_overlap_segments( |
| 146 | seg.waypoints, | 148 | seg.waypoints, |
| 147 | 5000 /* meters max total dist until a new split is forced */, | 149 | 5000 /* meters max total dist until a new split is forced */, |
| 148 | splits_with_overlap_segments); | 150 | splits_with_overlap_segments); |
| 149 | auto const before_query = chrono::steady_clock::now(); | 151 | auto const before_query = chrono::steady_clock::now(); |
| 150 | 152 | ||
| 151 | l_.debug("Querying for relevant situations"); | 153 | l_.debug("Querying for relevant situations"); |
| 152 | auto relevant_road_closures = | 154 | auto relevant_road_closures = |
| 153 | std::unordered_set<std::shared_ptr<datex2::road_closure>>{}; | 155 | std::unordered_set<std::shared_ptr<datex2::road_closure>>{}; |
| 154 | auto ls_checked = 0uz; | 156 | auto ls_checked = 0uz; |
| 155 | auto p_checked = 0uz; | 157 | auto p_checked = 0uz; |
| 156 | auto i = 0; | 158 | auto i = 0; |
| @@ -197,66 +199,70 @@ auto handler::process_gpx(gpx::file&& gpx_file) | |||
| 197 | 199 | ||
| 198 | auto const after_query = chrono::steady_clock::now(); | 200 | auto const after_query = chrono::steady_clock::now(); |
| 199 | l_.debug( | 201 | l_.debug( |
| 200 | "Done (checked {} line string(s) and {} point(s)) in {}", ls_checked, | 202 | "Done (checked {} line string(s) and {} point(s)) in {}", ls_checked, |
| 201 | p_checked, | 203 | p_checked, |
| 202 | chrono::duration_cast<chrono::milliseconds>(after_query - before_query)); | 204 | chrono::duration_cast<chrono::milliseconds>(after_query - before_query)); |
| 203 | 205 | ||
| 204 | auto relevant_situations = | 206 | auto relevant_situations = |
| 205 | std::unordered_set<std::shared_ptr<datex2::situation>>{}; | 207 | std::unordered_set<std::shared_ptr<datex2::situation>>{}; |
| 206 | for (auto const& rc : relevant_road_closures) | 208 | for (auto const& rc : relevant_road_closures) |
| 207 | relevant_situations.emplace(rc->parent); | 209 | relevant_situations.emplace(rc->parent); |
| 208 | 210 | ||
| 209 | l_.debug( | 211 | l_.debug( |
| 210 | "Identified {} relevant road closure(s), part of {} unique " | 212 | "Identified {} relevant road closure(s), part of {} unique " |
| 211 | "situation(s)", | 213 | "situation(s)", |
| 212 | relevant_road_closures.size(), relevant_situations.size()); | 214 | relevant_road_closures.size(), relevant_situations.size()); |
| 213 | for (auto const& sit : relevant_situations) | 215 | for (auto const& sit : relevant_situations) |
| 214 | l_.debug("Relevant situation: {}", sit->id); | 216 | l_.debug("Relevant situation: {}", sit->id); |
| 215 | 217 | ||
| 216 | return process_gpx_result{ | 218 | return process_gpx_result{ |
| 217 | .tracks = gpx_file.tracks | 219 | .tracks = gpx_file.tracks |
| 218 | | views::transform( | 220 | | views::transform( |
| 219 | [](auto const& trk) -> track | 221 | [](auto const& trk) -> track |
| 220 | { | 222 | { |
| 221 | return { | 223 | return { |
| 222 | .segments = trk.segments | 224 | .segments = |
| 223 | | views::transform( | 225 | trk.segments |
| 224 | [](auto const& seg) -> track_segment | 226 | | views::transform( |
| 225 | { return {.points = seg.waypoints}; }) | 227 | [](auto const& seg) -> track_segment |
| 226 | | std::ranges::to<std::vector<track_segment>>(), | 228 | { return {.points = seg.waypoints}; }) |
| 227 | }; | 229 | | std::ranges::to<std::vector<track_segment>>(), |
| 228 | }) | 230 | }; |
| 231 | }) | ||
| 229 | | std::ranges::to<std::vector<track>>(), | 232 | | std::ranges::to<std::vector<track>>(), |
| 230 | .relevant_situations = | 233 | .relevant_situations = |
| 231 | relevant_situations | 234 | relevant_situations |
| 232 | | views::transform( | 235 | | views::transform( |
| 233 | [&](std::shared_ptr<datex2::situation> sit) -> relevant_situation | 236 | [&](std::shared_ptr<datex2::situation> sit) -> relevant_situation |
| 234 | { | 237 | { |
| 235 | return { | 238 | return { |
| 236 | .id = sit->id, | 239 | .id = sit->id, |
| 237 | .location = sit->location, | 240 | .location = sit->location, |
| 238 | .comments = sit->comments, | 241 | .comments = sit->comments, |
| 239 | .relevant_road_closures = | 242 | .relevant_road_closures = |
| 240 | relevant_road_closures | 243 | relevant_road_closures |
| 241 | | views::filter( | 244 | | views::filter( |
| 242 | [&](std::shared_ptr<datex2::road_closure> const& rc) -> bool | 245 | [&](std::shared_ptr<datex2::road_closure> const& rc) |
| 243 | { return std::shared_ptr{rc->parent} == sit; }) | 246 | -> bool |
| 244 | | views::transform( | 247 | { return std::shared_ptr{rc->parent} == sit; }) |
| 245 | [](std::shared_ptr<datex2::road_closure> const& rc) | 248 | | views::transform( |
| 246 | -> relevant_road_closure | 249 | [](std::shared_ptr<datex2::road_closure> const& rc) |
| 247 | { | 250 | -> relevant_road_closure |
| 248 | return { | 251 | { |
| 249 | .relevant_lss = | 252 | return { |
| 250 | rc->relevant_line_strings | 253 | .relevant_lss = |
| 251 | | views::transform( | 254 | rc->relevant_line_strings |
| 252 | [](auto const& lsp) -> geo::linestring { return *lsp; }) | 255 | | views::transform( |
| 253 | | std::ranges::to<std::vector<geo::linestring>>(), | 256 | [](auto const& lsp) -> geo::linestring |
| 254 | }; | 257 | { return *lsp; }) |
| 255 | }) | 258 | | std::ranges::to< |
| 256 | | std::ranges::to<std::vector<relevant_road_closure>>(), | 259 | std::vector<geo::linestring>>(), |
| 257 | }; | 260 | }; |
| 258 | }) | 261 | }) |
| 259 | | std::ranges::to<std::vector<relevant_situation>>(), | 262 | | std::ranges::to<std::vector<relevant_road_closure>>(), |
| 263 | }; | ||
| 264 | }) | ||
| 265 | | std::ranges::to<std::vector<relevant_situation>>(), | ||
| 260 | }; | 266 | }; |
| 261 | } | 267 | } |
| 262 | 268 | ||
diff --git a/server/src/api.cppm b/server/src/api.cppm index c724a45..66e8f45 100644 --- a/server/src/api.cppm +++ b/server/src/api.cppm | |||
| @@ -58,28 +58,30 @@ struct sysinfo | |||
| 58 | }; | 58 | }; |
| 59 | 59 | ||
| 60 | auto tag_invoke( | 60 | auto tag_invoke( |
| 61 | json::value_from_tag, json::value& jv, relevant_road_closure const& clo) | 61 | json::value_from_tag, json::value& jv, relevant_road_closure const& clo) |
| 62 | -> void; | 62 | -> void; |
| 63 | auto tag_invoke( | 63 | auto tag_invoke( |
| 64 | json::value_from_tag, json::value& jv, relevant_situation const& sit) -> void; | 64 | json::value_from_tag, json::value& jv, relevant_situation const& sit) |
| 65 | -> void; | ||
| 65 | auto tag_invoke(json::value_from_tag, json::value& jv, track_segment const& seg) | 66 | auto tag_invoke(json::value_from_tag, json::value& jv, track_segment const& seg) |
| 66 | -> void; | 67 | -> void; |
| 67 | auto tag_invoke(json::value_from_tag, json::value& jv, track const& track) | 68 | auto tag_invoke(json::value_from_tag, json::value& jv, track const& track) |
| 68 | -> void; | 69 | -> void; |
| 69 | auto tag_invoke( | 70 | auto tag_invoke( |
| 70 | json::value_from_tag, json::value& jv, process_gpx_result const& res) -> void; | 71 | json::value_from_tag, json::value& jv, process_gpx_result const& res) |
| 72 | -> void; | ||
| 71 | auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) | 73 | auto tag_invoke(json::value_from_tag, json::value& jv, sysinfo const& info) |
| 72 | -> void; | 74 | -> void; |
| 73 | 75 | ||
| 74 | class handler | 76 | class handler |
| 75 | { | 77 | { |
| 76 | using lse_index_value = std::tuple< | 78 | using lse_index_value = std::tuple< |
| 77 | geo::box, std::shared_ptr<geo::linestring>, | 79 | geo::box, std::shared_ptr<geo::linestring>, |
| 78 | std::shared_ptr<datex2::road_closure>>; | 80 | std::shared_ptr<datex2::road_closure>>; |
| 79 | using p_index_value = | 81 | using p_index_value = |
| 80 | std::pair<geo::point, std::shared_ptr<datex2::road_closure>>; | 82 | std::pair<geo::point, std::shared_ptr<datex2::road_closure>>; |
| 81 | using lse_index = | 83 | using lse_index = |
| 82 | bgeo::index::rtree<lse_index_value, bgeo::index::quadratic<16>>; | 84 | bgeo::index::rtree<lse_index_value, bgeo::index::quadratic<16>>; |
| 83 | using p_index = bgeo::index::rtree<p_index_value, bgeo::index::quadratic<16>>; | 85 | using p_index = bgeo::index::rtree<p_index_value, bgeo::index::quadratic<16>>; |
| 84 | 86 | ||
| 85 | log::logger l_; | 87 | log::logger l_; |
diff --git a/server/src/config.cpp b/server/src/config.cpp index e9a728b..eab97b5 100644 --- a/server/src/config.cpp +++ b/server/src/config.cpp | |||
| @@ -17,7 +17,7 @@ namespace routemon::config { | |||
| 17 | class location | 17 | class location |
| 18 | { | 18 | { |
| 19 | std::optional<std::pair<std::string_view, util::not_null<location const*>>> | 19 | std::optional<std::pair<std::string_view, util::not_null<location const*>>> |
| 20 | next_; | 20 | next_; |
| 21 | 21 | ||
| 22 | auto append_to(std::string& s) const -> void | 22 | auto append_to(std::string& s) const -> void |
| 23 | { | 23 | { |
| @@ -64,7 +64,7 @@ public: | |||
| 64 | catch (boost::system::system_error const&) | 64 | catch (boost::system::system_error const&) |
| 65 | { | 65 | { |
| 66 | throw std::runtime_error{std::format( | 66 | throw std::runtime_error{std::format( |
| 67 | "expected an object at {}", loc.to_string())}; | 67 | "expected an object at {}", loc.to_string())}; |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | auto check_unused() const -> void | 70 | auto check_unused() const -> void |
| @@ -74,7 +74,7 @@ public: | |||
| 74 | if (!visited_.contains(std::string{kv.key()})) | 74 | if (!visited_.contains(std::string{kv.key()})) |
| 75 | { | 75 | { |
| 76 | throw std::runtime_error{std::format( | 76 | throw std::runtime_error{std::format( |
| 77 | "unexpected key {}", loc_.sub(kv.key()).to_string())}; | 77 | "unexpected key {}", loc_.sub(kv.key()).to_string())}; |
| 78 | } | 78 | } |
| 79 | } | 79 | } |
| 80 | } | 80 | } |
| @@ -93,21 +93,21 @@ public: | |||
| 93 | catch (boost::system::system_error const& e) | 93 | catch (boost::system::system_error const& e) |
| 94 | { | 94 | { |
| 95 | throw std::runtime_error{std::format( | 95 | throw std::runtime_error{std::format( |
| 96 | "failed to read {}: {}", loc.to_string(), e.code().message())}; | 96 | "failed to read {}: {}", loc.to_string(), e.code().message())}; |
| 97 | } | 97 | } |
| 98 | } | 98 | } |
| 99 | else | 99 | else |
| 100 | { | 100 | { |
| 101 | throw std::runtime_error{std::format( | 101 | throw std::runtime_error{std::format( |
| 102 | "did not find expected key {}", loc.to_string())}; | 102 | "did not find expected key {}", loc.to_string())}; |
| 103 | } | 103 | } |
| 104 | } | 104 | } |
| 105 | }; | 105 | }; |
| 106 | 106 | ||
| 107 | auto as_checked_object( | 107 | auto as_checked_object( |
| 108 | json::value const& jv, location const& loc, | 108 | json::value const& jv, location const& loc, |
| 109 | std::invocable<object_reader&> auto const& f) | 109 | std::invocable<object_reader&> auto const& f) |
| 110 | -> decltype(f(std::declval<object_reader&>())) | 110 | -> decltype(f(std::declval<object_reader&>())) |
| 111 | { | 111 | { |
| 112 | auto r = object_reader{jv, loc}; | 112 | auto r = object_reader{jv, loc}; |
| 113 | auto&& v = f(r); | 113 | auto&& v = f(r); |
| @@ -116,109 +116,110 @@ auto as_checked_object( | |||
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | auto tag_invoke( | 118 | auto tag_invoke( |
| 119 | json::value_to_tag<rwgps> const&, json::value const& jv, location const& loc) | 119 | json::value_to_tag<rwgps> const&, json::value const& jv, |
| 120 | -> rwgps | 120 | location const& loc) -> rwgps |
| 121 | { | 121 | { |
| 122 | return as_checked_object( | 122 | return as_checked_object( |
| 123 | jv, loc, | 123 | jv, loc, |
| 124 | [](object_reader& r) -> rwgps | 124 | [](object_reader& r) -> rwgps |
| 125 | { | 125 | { |
| 126 | return { | 126 | return { |
| 127 | .api_key = r.expect_at<std::string>("api_key"), | 127 | .api_key = r.expect_at<std::string>("api_key"), |
| 128 | .auth_token = r.expect_at<std::string>("auth_token"), | 128 | .auth_token = r.expect_at<std::string>("auth_token"), |
| 129 | }; | 129 | }; |
| 130 | }); | 130 | }); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| 133 | auto tag_invoke( | 133 | auto tag_invoke( |
| 134 | json::value_to_tag<situations> const&, json::value const& jv, | 134 | json::value_to_tag<situations> const&, json::value const& jv, |
| 135 | location const& loc) -> situations | 135 | location const& loc) -> situations |
| 136 | { | 136 | { |
| 137 | return as_checked_object( | 137 | return as_checked_object( |
| 138 | jv, loc, | 138 | jv, loc, |
| 139 | [](object_reader& r) -> situations | 139 | [](object_reader& r) -> situations |
| 140 | { | 140 | { |
| 141 | return { | 141 | return { |
| 142 | .datex2_filename = r.expect_at<std::string>("datex2_filename"), | 142 | .datex2_filename = r.expect_at<std::string>("datex2_filename"), |
| 143 | }; | 143 | }; |
| 144 | }); | 144 | }); |
| 145 | } | 145 | } |
| 146 | 146 | ||
| 147 | auto tag_invoke( | 147 | auto tag_invoke( |
| 148 | json::value_to_tag<database> const&, json::value const& jv, | 148 | json::value_to_tag<database> const&, json::value const& jv, |
| 149 | location const& loc) -> database | 149 | location const& loc) -> database |
| 150 | { | 150 | { |
| 151 | return as_checked_object( | 151 | return as_checked_object( |
| 152 | jv, loc, | 152 | jv, loc, |
| 153 | [](object_reader& r) -> database | 153 | [](object_reader& r) -> database |
| 154 | { | 154 | { |
| 155 | return { | 155 | return { |
| 156 | .sqlite3_filename = r.expect_at<std::string>("sqlite3_filename"), | 156 | .sqlite3_filename = r.expect_at<std::string>("sqlite3_filename"), |
| 157 | }; | 157 | }; |
| 158 | }); | 158 | }); |
| 159 | } | 159 | } |
| 160 | 160 | ||
| 161 | auto tag_invoke( | 161 | auto tag_invoke( |
| 162 | json::value_to_tag<http_server> const&, json::value const& jv, | 162 | json::value_to_tag<http_server> const&, json::value const& jv, |
| 163 | location const& loc) -> http_server | 163 | location const& loc) -> http_server |
| 164 | { | 164 | { |
| 165 | return as_checked_object( | 165 | return as_checked_object( |
| 166 | jv, loc, | 166 | jv, loc, |
| 167 | [](object_reader& r) -> http_server | 167 | [](object_reader& r) -> http_server |
| 168 | { | 168 | { |
| 169 | return { | 169 | return { |
| 170 | .lax_cors = r.expect_at<bool>("lax_cors"), | 170 | .lax_cors = r.expect_at<bool>("lax_cors"), |
| 171 | }; | 171 | }; |
| 172 | }); | 172 | }); |
| 173 | } | 173 | } |
| 174 | 174 | ||
| 175 | auto tag_invoke( | 175 | auto tag_invoke( |
| 176 | json::value_to_tag<logger> const&, json::value const& jv, location const& loc) | 176 | json::value_to_tag<logger> const&, json::value const& jv, |
| 177 | -> logger | 177 | location const& loc) -> logger |
| 178 | { | 178 | { |
| 179 | return as_checked_object( | 179 | return as_checked_object( |
| 180 | jv, loc, | 180 | jv, loc, |
| 181 | [obj_loc = loc](object_reader& r) -> logger | 181 | [obj_loc = loc](object_reader& r) -> logger |
| 182 | { | 182 | { |
| 183 | auto const level_str = r.expect_at<std::string>("level"); | 183 | auto const level_str = r.expect_at<std::string>("level"); |
| 184 | auto level = log::level{}; | 184 | auto level = log::level{}; |
| 185 | if (level_str == "debug") | 185 | if (level_str == "debug") |
| 186 | level = log::level::debug; | 186 | level = log::level::debug; |
| 187 | else if (level_str == "info") | 187 | else if (level_str == "info") |
| 188 | level = log::level::info; | 188 | level = log::level::info; |
| 189 | else if (level_str == "warn") | 189 | else if (level_str == "warn") |
| 190 | level = log::level::warn; | 190 | level = log::level::warn; |
| 191 | else if (level_str == "error") | 191 | else if (level_str == "error") |
| 192 | level = log::level::error; | 192 | level = log::level::error; |
| 193 | else | 193 | else |
| 194 | throw std::runtime_error{std::format( | 194 | throw std::runtime_error{std::format( |
| 195 | "unable to parse log level {:?} at {}: expected one " | 195 | "unable to parse log level {:?} at {}: expected one " |
| 196 | "of {{debug, info, warn, error}}", | 196 | "of {{debug, info, warn, error}}", |
| 197 | level_str, obj_loc.sub("level").to_string())}; | 197 | level_str, obj_loc.sub("level").to_string())}; |
| 198 | return logger{level}; | 198 | return logger{level}; |
| 199 | }); | 199 | }); |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | auto json_value_to_app(json::value const& jv) -> app | 202 | auto json_value_to_app(json::value const& jv) -> app |
| 203 | { | 203 | { |
| 204 | return as_checked_object( | 204 | return as_checked_object( |
| 205 | jv, location{}, | 205 | jv, location{}, |
| 206 | [](object_reader& r) -> app | 206 | [](object_reader& r) -> app |
| 207 | { | 207 | { |
| 208 | return { | 208 | return { |
| 209 | .rwgps = r.expect_at<rwgps>("rwgps"), | 209 | .rwgps = r.expect_at<rwgps>("rwgps"), |
| 210 | .situations = r.expect_at<situations>("situations"), | 210 | .situations = r.expect_at<situations>("situations"), |
| 211 | .database = r.expect_at<database>("database"), | 211 | .database = r.expect_at<database>("database"), |
| 212 | .http_server = r.expect_at<http_server>("http_server"), | 212 | .http_server = r.expect_at<http_server>("http_server"), |
| 213 | .logger = r.expect_at<logger>("logger"), | 213 | .logger = r.expect_at<logger>("logger"), |
| 214 | }; | 214 | }; |
| 215 | }); | 215 | }); |
| 216 | } | 216 | } |
| 217 | 217 | ||
| 218 | auto load_file(std::string const& filename) -> app | 218 | auto load_file(std::string const& filename) -> app |
| 219 | { | 219 | { |
| 220 | auto f = | 220 | auto f = std::ifstream{ |
| 221 | std::ifstream{filename}; // TODO: ensure that we are opening in binary mode? | 221 | filename |
| 222 | }; // TODO: ensure that we are opening in binary mode? | ||
| 222 | if (!f.is_open()) | 223 | if (!f.is_open()) |
| 223 | throw std::runtime_error{std::format("failed to open {}", filename)}; | 224 | throw std::runtime_error{std::format("failed to open {}", filename)}; |
| 224 | auto jv = json::value{}; | 225 | auto jv = json::value{}; |
| @@ -229,7 +230,7 @@ auto load_file(std::string const& filename) -> app | |||
| 229 | catch (boost::system::system_error const& e) | 230 | catch (boost::system::system_error const& e) |
| 230 | { | 231 | { |
| 231 | throw std::runtime_error{std::format( | 232 | throw std::runtime_error{std::format( |
| 232 | "failed to parse: {}", e.code().message())}; | 233 | "failed to parse: {}", e.code().message())}; |
| 233 | } | 234 | } |
| 234 | return json_value_to_app(jv); | 235 | return json_value_to_app(jv); |
| 235 | } | 236 | } |
diff --git a/server/src/database.cppm b/server/src/database.cppm index c31d610..86ed0d7 100644 --- a/server/src/database.cppm +++ b/server/src/database.cppm | |||
| @@ -31,15 +31,15 @@ export auto open(std::string const& filename) -> std::shared_ptr<connection> | |||
| 31 | if (version != expected_database_version) | 31 | if (version != expected_database_version) |
| 32 | { | 32 | { |
| 33 | throw std::runtime_error{std::format( | 33 | throw std::runtime_error{std::format( |
| 34 | "database migration version ({}) does not match expected " | 34 | "database migration version ({}) does not match expected " |
| 35 | "version ({}), consider running migrations", | 35 | "version ({}), consider running migrations", |
| 36 | *version, expected_database_version)}; | 36 | *version, expected_database_version)}; |
| 37 | } | 37 | } |
| 38 | } | 38 | } |
| 39 | catch (std::exception const& e) | 39 | catch (std::exception const& e) |
| 40 | { | 40 | { |
| 41 | throw std::runtime_error{std::format( | 41 | throw std::runtime_error{std::format( |
| 42 | "failed to query database version: {}", e.what())}; | 42 | "failed to query database version: {}", e.what())}; |
| 43 | } | 43 | } |
| 44 | return std::shared_ptr<connection>{new connection{std::move(dbc)}}; | 44 | return std::shared_ptr<connection>{new connection{std::move(dbc)}}; |
| 45 | } | 45 | } |
diff --git a/server/src/datex2.cppm b/server/src/datex2.cppm index 7c20347..d7b4921 100644 --- a/server/src/datex2.cppm +++ b/server/src/datex2.cppm | |||
| @@ -31,7 +31,7 @@ export struct situation | |||
| 31 | { | 31 | { |
| 32 | std::string id; | 32 | std::string id; |
| 33 | std::optional<geo::point> location = | 33 | std::optional<geo::point> location = |
| 34 | std::nullopt; // as shown on the map, not used for querying | 34 | std::nullopt; // as shown on the map, not used for querying |
| 35 | std::vector<std::string> comments = {}; | 35 | std::vector<std::string> comments = {}; |
| 36 | std::vector<std::shared_ptr<road_closure>> road_closures = {}; | 36 | std::vector<std::shared_ptr<road_closure>> road_closures = {}; |
| 37 | }; | 37 | }; |
| @@ -54,13 +54,13 @@ export class loader | |||
| 54 | { | 54 | { |
| 55 | // ETRS 89 (EPSG:4258) -> WGS 84 (EPSG:4326) | 55 | // ETRS 89 (EPSG:4258) -> WGS 84 (EPSG:4326) |
| 56 | bgeo::srs::transformation< | 56 | bgeo::srs::transformation< |
| 57 | bgeo::srs::static_epsg<4258>, bgeo::srs::static_epsg<4326>> | 57 | bgeo::srs::static_epsg<4258>, bgeo::srs::static_epsg<4326>> |
| 58 | etrs89_to_wgs84_{}; | 58 | etrs89_to_wgs84_{}; |
| 59 | 59 | ||
| 60 | std::multiset<std::string> warnings_; | 60 | std::multiset<std::string> warnings_; |
| 61 | 61 | ||
| 62 | auto add_location_from_xml(road_closure& rc, pugi::xml_node const& loc_xml) | 62 | auto add_location_from_xml(road_closure& rc, pugi::xml_node const& loc_xml) |
| 63 | -> void | 63 | -> void |
| 64 | { | 64 | { |
| 65 | auto loc_xml_type = std::string_view{loc_xml.attribute("xsi:type").value()}; | 65 | auto loc_xml_type = std::string_view{loc_xml.attribute("xsi:type").value()}; |
| 66 | if (loc_xml_type == "loc:ItineraryByIndexedLocations") | 66 | if (loc_xml_type == "loc:ItineraryByIndexedLocations") |
| @@ -79,15 +79,15 @@ export class loader | |||
| 79 | return; | 79 | return; |
| 80 | 80 | ||
| 81 | auto const srs_name = | 81 | auto const srs_name = |
| 82 | std::string_view{loc_gml_xml.attribute("srsName").value()}; | 82 | std::string_view{loc_gml_xml.attribute("srsName").value()}; |
| 83 | if (srs_name != "WGS 84"sv) | 83 | if (srs_name != "WGS 84"sv) |
| 84 | { | 84 | { |
| 85 | warnings_.insert( | 85 | warnings_.insert( |
| 86 | std::format("don't now how to handle the CRS {}", srs_name)); | 86 | std::format("don't now how to handle the CRS {}", srs_name)); |
| 87 | return; | 87 | return; |
| 88 | } | 88 | } |
| 89 | auto const pos_list_str = | 89 | auto const pos_list_str = |
| 90 | std::string_view{loc_gml_xml.child_value("loc:posList")}; | 90 | std::string_view{loc_gml_xml.child_value("loc:posList")}; |
| 91 | // lat1 long1 lat2 long2 ... lat(n-1) long(n-1) latn longn | 91 | // lat1 long1 lat2 long2 ... lat(n-1) long(n-1) latn longn |
| 92 | 92 | ||
| 93 | auto ls = std::make_shared<geo::linestring>(); | 93 | auto ls = std::make_shared<geo::linestring>(); |
| @@ -97,13 +97,13 @@ export class loader | |||
| 97 | for (auto const lat_or_long_str : std::views::split(pos_list_str, " "sv)) | 97 | for (auto const lat_or_long_str : std::views::split(pos_list_str, " "sv)) |
| 98 | { | 98 | { |
| 99 | auto mlat_or_long = | 99 | auto mlat_or_long = |
| 100 | util::parse_double(std::string_view{lat_or_long_str}); | 100 | util::parse_double(std::string_view{lat_or_long_str}); |
| 101 | if (!mlat_or_long) | 101 | if (!mlat_or_long) |
| 102 | { | 102 | { |
| 103 | warnings_.insert( | 103 | warnings_.insert( |
| 104 | std::format( | 104 | std::format( |
| 105 | "failed to parse coordinate {:?}", | 105 | "failed to parse coordinate {:?}", |
| 106 | std::string_view{lat_or_long_str})); | 106 | std::string_view{lat_or_long_str})); |
| 107 | return; | 107 | return; |
| 108 | } | 108 | } |
| 109 | 109 | ||
| @@ -131,7 +131,7 @@ export class loader | |||
| 131 | else if (loc_xml_type == "loc:PointLocation") | 131 | else if (loc_xml_type == "loc:PointLocation") |
| 132 | { | 132 | { |
| 133 | auto const& coords_xml = | 133 | auto const& coords_xml = |
| 134 | loc_xml.child("loc:pointByCoordinates").child("loc:pointCoordinates"); | 134 | loc_xml.child("loc:pointByCoordinates").child("loc:pointCoordinates"); |
| 135 | if (!coords_xml) | 135 | if (!coords_xml) |
| 136 | return; | 136 | return; |
| 137 | 137 | ||
| @@ -157,28 +157,28 @@ export class loader | |||
| 157 | else | 157 | else |
| 158 | { | 158 | { |
| 159 | warnings_.insert( | 159 | warnings_.insert( |
| 160 | std::format( | 160 | std::format( |
| 161 | "don't know how to hande location of type {}, ignoring", | 161 | "don't know how to hande location of type {}, ignoring", |
| 162 | loc_xml.attribute("xsi:type").value())); | 162 | loc_xml.attribute("xsi:type").value())); |
| 163 | return; | 163 | return; |
| 164 | } | 164 | } |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | auto handle_road_or_carriageway_or_lane_management( | 167 | auto handle_road_or_carriageway_or_lane_management( |
| 168 | pugi::xml_node const& record_xml, std::weak_ptr<situation> parent) | 168 | pugi::xml_node const& record_xml, std::weak_ptr<situation> parent) |
| 169 | -> std::optional<std::shared_ptr<road_closure>> | 169 | -> std::optional<std::shared_ptr<road_closure>> |
| 170 | { | 170 | { |
| 171 | auto const type = | 171 | auto const type = |
| 172 | std::string_view{record_xml | 172 | std::string_view{record_xml |
| 173 | .child("sit:roadOrCarriagewayOrLaneManagementType") | 173 | .child("sit:roadOrCarriagewayOrLaneManagementType") |
| 174 | .child_value()}; | 174 | .child_value()}; |
| 175 | if (type != "carriagewayClosures" && type != "roadClosed") | 175 | if (type != "carriagewayClosures" && type != "roadClosed") |
| 176 | // TODO: checken of er nog andere types fietsers de doorgang zouden | 176 | // TODO: checken of er nog andere types fietsers de doorgang zouden |
| 177 | // kunnen blokkeren? | 177 | // kunnen blokkeren? |
| 178 | return std::nullopt; | 178 | return std::nullopt; |
| 179 | 179 | ||
| 180 | auto const& restricted_vehicle_types_xml = | 180 | auto const& restricted_vehicle_types_xml = |
| 181 | record_xml.child("sit:forVehiclesWithCharacteristicsOf"); | 181 | record_xml.child("sit:forVehiclesWithCharacteristicsOf"); |
| 182 | bool likely_restriction_for_bikes = restricted_vehicle_types_xml.empty(); | 182 | bool likely_restriction_for_bikes = restricted_vehicle_types_xml.empty(); |
| 183 | for (auto const vehicle_type_xml : | 183 | for (auto const vehicle_type_xml : |
| 184 | restricted_vehicle_types_xml.children("com:vehicleType")) | 184 | restricted_vehicle_types_xml.children("com:vehicleType")) |
| @@ -199,20 +199,20 @@ export class loader | |||
| 199 | auto const& validity_xml = record_xml.child("sit:validity"); | 199 | auto const& validity_xml = record_xml.child("sit:validity"); |
| 200 | if (validity_xml | 200 | if (validity_xml |
| 201 | && validity_xml.child_value("com:validityStatus") | 201 | && validity_xml.child_value("com:validityStatus") |
| 202 | == "definedByValidityTimeSpec"sv) | 202 | == "definedByValidityTimeSpec"sv) |
| 203 | { | 203 | { |
| 204 | auto const& validity_spec_xml = | 204 | auto const& validity_spec_xml = |
| 205 | validity_xml.child("com:validityTimeSpecification"); | 205 | validity_xml.child("com:validityTimeSpecification"); |
| 206 | 206 | ||
| 207 | auto valid_periods = std::vector<time::period>{}; | 207 | auto valid_periods = std::vector<time::period>{}; |
| 208 | auto exception_periods = std::vector<time::period>{}; | 208 | auto exception_periods = std::vector<time::period>{}; |
| 209 | 209 | ||
| 210 | // TODO: com:overallEndTime may be missing (according to the DATEX | 210 | // TODO: com:overallEndTime may be missing (according to the DATEX |
| 211 | // II v3 data model) | 211 | // II v3 data model) |
| 212 | auto const overall_start_time = | 212 | auto const overall_start_time = parse_timestamp( |
| 213 | parse_timestamp(validity_spec_xml.child_value("com:overallStartTime")); | 213 | validity_spec_xml.child_value("com:overallStartTime")); |
| 214 | auto const overall_end_time = | 214 | auto const overall_end_time = |
| 215 | parse_timestamp(validity_spec_xml.child_value("com:overallEndTime")); | 215 | parse_timestamp(validity_spec_xml.child_value("com:overallEndTime")); |
| 216 | if (overall_start_time && overall_end_time | 216 | if (overall_start_time && overall_end_time |
| 217 | && *overall_start_time < *overall_end_time) | 217 | && *overall_start_time < *overall_end_time) |
| 218 | { | 218 | { |
| @@ -221,10 +221,10 @@ export class loader | |||
| 221 | for (auto const valid_period_xml : | 221 | for (auto const valid_period_xml : |
| 222 | validity_xml.children("com:validPeriod")) | 222 | validity_xml.children("com:validPeriod")) |
| 223 | { | 223 | { |
| 224 | auto const start_of_period = | 224 | auto const start_of_period = parse_timestamp( |
| 225 | parse_timestamp(valid_period_xml.child_value("com:startOfPeriod")); | 225 | valid_period_xml.child_value("com:startOfPeriod")); |
| 226 | auto const end_of_period = | 226 | auto const end_of_period = |
| 227 | parse_timestamp(valid_period_xml.child_value("com:endOfPeriod")); | 227 | parse_timestamp(valid_period_xml.child_value("com:endOfPeriod")); |
| 228 | if (start_of_period && end_of_period | 228 | if (start_of_period && end_of_period |
| 229 | && *start_of_period < *end_of_period) | 229 | && *start_of_period < *end_of_period) |
| 230 | { | 230 | { |
| @@ -235,9 +235,9 @@ export class loader | |||
| 235 | validity_xml.children("com:exceptionPeriod")) | 235 | validity_xml.children("com:exceptionPeriod")) |
| 236 | { | 236 | { |
| 237 | auto const start_of_period = parse_timestamp( | 237 | auto const start_of_period = parse_timestamp( |
| 238 | exception_period_xml.child_value("com:startOfPeriod")); | 238 | exception_period_xml.child_value("com:startOfPeriod")); |
| 239 | auto const end_of_period = parse_timestamp( | 239 | auto const end_of_period = parse_timestamp( |
| 240 | exception_period_xml.child_value("com:endOfPeriod")); | 240 | exception_period_xml.child_value("com:endOfPeriod")); |
| 241 | if (start_of_period && end_of_period | 241 | if (start_of_period && end_of_period |
| 242 | && *start_of_period < *end_of_period) | 242 | && *start_of_period < *end_of_period) |
| 243 | { | 243 | { |
| @@ -246,19 +246,19 @@ export class loader | |||
| 246 | } | 246 | } |
| 247 | 247 | ||
| 248 | validity = | 248 | validity = |
| 249 | time::period_seq{valid_periods.begin(), valid_periods.end()}.except( | 249 | time::period_seq{valid_periods.begin(), valid_periods.end()}.except( |
| 250 | time::period_seq{ | 250 | time::period_seq{ |
| 251 | exception_periods.begin(), exception_periods.end() | 251 | exception_periods.begin(), exception_periods.end() |
| 252 | }); | 252 | }); |
| 253 | } | 253 | } |
| 254 | else | 254 | else |
| 255 | { | 255 | { |
| 256 | warnings_.insert( | 256 | warnings_.insert( |
| 257 | std::format( | 257 | std::format( |
| 258 | "invalid overall start / end time (start time: {}, end " | 258 | "invalid overall start / end time (start time: {}, end " |
| 259 | "time: {})", | 259 | "time: {})", |
| 260 | validity_spec_xml.child_value("com:overallStartTime"), | 260 | validity_spec_xml.child_value("com:overallStartTime"), |
| 261 | validity_spec_xml.child_value("com:overallEndTime"))); | 261 | validity_spec_xml.child_value("com:overallEndTime"))); |
| 262 | return std::nullopt; | 262 | return std::nullopt; |
| 263 | } | 263 | } |
| 264 | } | 264 | } |
| @@ -272,7 +272,7 @@ export class loader | |||
| 272 | 272 | ||
| 273 | public: | 273 | public: |
| 274 | [[nodiscard]] auto load_situation_publication(std::string const& filename) | 274 | [[nodiscard]] auto load_situation_publication(std::string const& filename) |
| 275 | -> situation_publication | 275 | -> situation_publication |
| 276 | { | 276 | { |
| 277 | auto doc = pugi::xml_document{}; | 277 | auto doc = pugi::xml_document{}; |
| 278 | if (auto result = doc.load_file(filename.c_str()); !result) | 278 | if (auto result = doc.load_file(filename.c_str()); !result) |
| @@ -281,7 +281,7 @@ public: | |||
| 281 | } | 281 | } |
| 282 | auto payload_xml = doc.child("mc:messageContainer").child("mc:payload"); | 282 | auto payload_xml = doc.child("mc:messageContainer").child("mc:payload"); |
| 283 | auto mpublication_time = | 283 | auto mpublication_time = |
| 284 | parse_timestamp(payload_xml.child_value("com:publicationTime")); | 284 | parse_timestamp(payload_xml.child_value("com:publicationTime")); |
| 285 | if (!mpublication_time) | 285 | if (!mpublication_time) |
| 286 | throw std::runtime_error{ | 286 | throw std::runtime_error{ |
| 287 | "provided publication does not name publication time" | 287 | "provided publication does not name publication time" |
| @@ -302,7 +302,7 @@ public: | |||
| 302 | for (auto const record_xml : sit_xml.children("sit:situationRecord")) | 302 | for (auto const record_xml : sit_xml.children("sit:situationRecord")) |
| 303 | { | 303 | { |
| 304 | auto const record_type = | 304 | auto const record_type = |
| 305 | std::string_view{record_xml.attribute("xsi:type").value()}; | 305 | std::string_view{record_xml.attribute("xsi:type").value()}; |
| 306 | auto const primary_record_types = std::unordered_set<std::string_view>{ | 306 | auto const primary_record_types = std::unordered_set<std::string_view>{ |
| 307 | "sit:Roadworks", | 307 | "sit:Roadworks", |
| 308 | /* { */ "sit:MaintenanceWorks", | 308 | /* { */ "sit:MaintenanceWorks", |
| @@ -320,8 +320,8 @@ public: | |||
| 320 | 320 | ||
| 321 | if (record_type == "sit:RoadOrCarriagewayOrLaneManagement") | 321 | if (record_type == "sit:RoadOrCarriagewayOrLaneManagement") |
| 322 | { | 322 | { |
| 323 | if (auto rc = | 323 | if (auto rc = handle_road_or_carriageway_or_lane_management( |
| 324 | handle_road_or_carriageway_or_lane_management(record_xml, sit)) | 324 | record_xml, sit)) |
| 325 | { | 325 | { |
| 326 | sit->road_closures.push_back(*rc); | 326 | sit->road_closures.push_back(*rc); |
| 327 | } | 327 | } |
| @@ -335,10 +335,10 @@ public: | |||
| 335 | // (comment_xml.child_value("sit:commentType") | 335 | // (comment_xml.child_value("sit:commentType") |
| 336 | // == "internalNote"sv) { | 336 | // == "internalNote"sv) { |
| 337 | auto candidate = std::optional<std::pair< | 337 | auto candidate = std::optional<std::pair< |
| 338 | std::string_view, std::string_view>>{}; // (text, language) | 338 | std::string_view, std::string_view>>{}; // (text, language) |
| 339 | for (auto const comment_value_xml : comment_xml.child("sit:comment") | 339 | for (auto const comment_value_xml : comment_xml.child("sit:comment") |
| 340 | .child("com:values") | 340 | .child("com:values") |
| 341 | .children("com:value")) | 341 | .children("com:value")) |
| 342 | { | 342 | { |
| 343 | if (!candidate | 343 | if (!candidate |
| 344 | || comment_value_xml.attribute("lang").value() == "nl"sv | 344 | || comment_value_xml.attribute("lang").value() == "nl"sv |
| @@ -346,8 +346,8 @@ public: | |||
| 346 | && comment_value_xml.attribute("lang").value() == "nl"sv)) | 346 | && comment_value_xml.attribute("lang").value() == "nl"sv)) |
| 347 | { | 347 | { |
| 348 | candidate = std::make_pair( | 348 | candidate = std::make_pair( |
| 349 | comment_value_xml.child_value(), | 349 | comment_value_xml.child_value(), |
| 350 | comment_value_xml.attribute("lang").value()); | 350 | comment_value_xml.attribute("lang").value()); |
| 351 | } | 351 | } |
| 352 | } | 352 | } |
| 353 | if (candidate) | 353 | if (candidate) |
| @@ -355,7 +355,7 @@ public: | |||
| 355 | auto already_present = false; | 355 | auto already_present = false; |
| 356 | for (auto const& comment : sit->comments) | 356 | for (auto const& comment : sit->comments) |
| 357 | already_present = | 357 | already_present = |
| 358 | already_present || comment == candidate->first; | 358 | already_present || comment == candidate->first; |
| 359 | if (!already_present) | 359 | if (!already_present) |
| 360 | { | 360 | { |
| 361 | sit->comments.emplace_back(candidate->first); | 361 | sit->comments.emplace_back(candidate->first); |
| @@ -365,19 +365,19 @@ public: | |||
| 365 | } | 365 | } |
| 366 | 366 | ||
| 367 | if (auto const location_ref_xml = | 367 | if (auto const location_ref_xml = |
| 368 | record_xml.child("sit:locationReference")) | 368 | record_xml.child("sit:locationReference")) |
| 369 | { | 369 | { |
| 370 | if (location_ref_xml.attribute("xsi:type").value() | 370 | if (location_ref_xml.attribute("xsi:type").value() |
| 371 | == "loc:PointLocation"sv) | 371 | == "loc:PointLocation"sv) |
| 372 | { | 372 | { |
| 373 | if (auto const coords_xml = | 373 | if (auto const coords_xml = |
| 374 | location_ref_xml.child("loc:pointByCoordinates") | 374 | location_ref_xml.child("loc:pointByCoordinates") |
| 375 | .child("loc:pointCoordinates")) | 375 | .child("loc:pointCoordinates")) |
| 376 | { | 376 | { |
| 377 | auto const mlat = | 377 | auto const mlat = |
| 378 | util::parse_double(coords_xml.child_value("loc:latitude")); | 378 | util::parse_double(coords_xml.child_value("loc:latitude")); |
| 379 | auto const mlon = | 379 | auto const mlon = |
| 380 | util::parse_double(coords_xml.child_value("loc:longitude")); | 380 | util::parse_double(coords_xml.child_value("loc:longitude")); |
| 381 | if (mlat && mlon) | 381 | if (mlat && mlon) |
| 382 | { | 382 | { |
| 383 | // Vaag genoeg zegt NDW dat het hier om WGS | 383 | // Vaag genoeg zegt NDW dat het hier om WGS |
diff --git a/server/src/geo.cppm b/server/src/geo.cppm index 9101af8..b599ad4 100644 --- a/server/src/geo.cppm +++ b/server/src/geo.cppm | |||
| @@ -9,15 +9,15 @@ export namespace bgeo = boost::geometry; | |||
| 9 | export namespace routemon::geo { | 9 | export namespace routemon::geo { |
| 10 | 10 | ||
| 11 | using point = | 11 | using point = |
| 12 | bgeo::model::point<double, 2, bgeo::cs::spherical_equatorial<bgeo::degree>>; | 12 | bgeo::model::point<double, 2, bgeo::cs::spherical_equatorial<bgeo::degree>>; |
| 13 | using linestring = bgeo::model::linestring<point>; | 13 | using linestring = bgeo::model::linestring<point>; |
| 14 | using box = bgeo::model::box<point>; | 14 | using box = bgeo::model::box<point>; |
| 15 | using stype = bgeo::srs::spheroid<double>; | 15 | using stype = bgeo::srs::spheroid<double>; |
| 16 | using vincenty_strategy = bgeo::strategy::distance::vincenty<stype>; | 16 | using vincenty_strategy = bgeo::strategy::distance::vincenty<stype>; |
| 17 | 17 | ||
| 18 | auto split_linestring_with_overlap_segments( | 18 | auto split_linestring_with_overlap_segments( |
| 19 | linestring const& ls, double max_split_distance_m, | 19 | linestring const& ls, double max_split_distance_m, |
| 20 | std::vector<linestring>& append_to) -> void | 20 | std::vector<linestring>& append_to) -> void |
| 21 | { | 21 | { |
| 22 | if (bgeo::is_empty(ls)) | 22 | if (bgeo::is_empty(ls)) |
| 23 | return; | 23 | return; |
| @@ -26,23 +26,23 @@ auto split_linestring_with_overlap_segments( | |||
| 26 | auto current_ls_length = 0.0; | 26 | auto current_ls_length = 0.0; |
| 27 | auto previous = std::optional<point>{}; | 27 | auto previous = std::optional<point>{}; |
| 28 | bgeo::for_each_point( | 28 | bgeo::for_each_point( |
| 29 | ls, | 29 | ls, |
| 30 | [&](point p) -> void | 30 | [&](point p) -> void |
| 31 | { | ||
| 32 | bgeo::append(current_ls, p); | ||
| 33 | if (previous) | ||
| 34 | { | 31 | { |
| 35 | auto d = bgeo::distance(*previous, p, vincenty_strategy()); | 32 | bgeo::append(current_ls, p); |
| 36 | current_ls_length += d; | 33 | if (previous) |
| 37 | if (current_ls_length > max_split_distance_m) | ||
| 38 | { | 34 | { |
| 39 | append_to.push_back(std::move(current_ls)); | 35 | auto d = bgeo::distance(*previous, p, vincenty_strategy()); |
| 40 | current_ls = linestring{*previous, p}; | 36 | current_ls_length += d; |
| 41 | current_ls_length = d; | 37 | if (current_ls_length > max_split_distance_m) |
| 38 | { | ||
| 39 | append_to.push_back(std::move(current_ls)); | ||
| 40 | current_ls = linestring{*previous, p}; | ||
| 41 | current_ls_length = d; | ||
| 42 | } | ||
| 42 | } | 43 | } |
| 43 | } | 44 | previous = p; |
| 44 | previous = p; | 45 | }); |
| 45 | }); | ||
| 46 | append_to.emplace_back(std::move(current_ls)); | 46 | append_to.emplace_back(std::move(current_ls)); |
| 47 | } | 47 | } |
| 48 | 48 | ||
diff --git a/server/src/gpx.cpp b/server/src/gpx.cpp index f70c133..078248f 100644 --- a/server/src/gpx.cpp +++ b/server/src/gpx.cpp | |||
| @@ -22,7 +22,7 @@ auto qname(std::string_view local) -> xml::qname_view | |||
| 22 | } | 22 | } |
| 23 | 23 | ||
| 24 | auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) | 24 | auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) |
| 25 | -> xml::parser<geo::point> | 25 | -> xml::parser<geo::point> |
| 26 | { | 26 | { |
| 27 | auto parse_xml_double = [](std::string_view sv) -> std::optional<double> | 27 | auto parse_xml_double = [](std::string_view sv) -> std::optional<double> |
| 28 | { return util::parse_double(sv, std::chars_format::fixed); }; | 28 | { return util::parse_double(sv, std::chars_format::fixed); }; |
| @@ -48,11 +48,11 @@ auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) | |||
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | auto parse_trkseg(xml::executor_ref e, xml::attribute_view) | 50 | auto parse_trkseg(xml::executor_ref e, xml::attribute_view) |
| 51 | -> xml::parser<track_segment> | 51 | -> xml::parser<track_segment> |
| 52 | { | 52 | { |
| 53 | auto s = track_segment{}; | 53 | auto s = track_segment{}; |
| 54 | while (auto mwpt = | 54 | while (auto mwpt = co_await allow_element( |
| 55 | co_await allow_element(e, qname("trkpt"), xml::hohalo<parse_wpt>())) | 55 | e, qname("trkpt"), xml::hohalo<parse_wpt>())) |
| 56 | bgeo::append(s.waypoints, *mwpt); | 56 | bgeo::append(s.waypoints, *mwpt); |
| 57 | co_return std::move(s); | 57 | co_return std::move(s); |
| 58 | } | 58 | } |
| @@ -61,20 +61,20 @@ auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track> | |||
| 61 | { | 61 | { |
| 62 | auto t = track{}; | 62 | auto t = track{}; |
| 63 | t.name = co_await allow_element( | 63 | t.name = co_await allow_element( |
| 64 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); | 64 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); |
| 65 | co_await allow_element( | 65 | co_await allow_element( |
| 66 | e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>()); | 66 | e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>()); |
| 67 | t.desc = co_await allow_element( | 67 | t.desc = co_await allow_element( |
| 68 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); | 68 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); |
| 69 | co_await xml::ignore_contents(e, /* until */ qname("trkseg")); | 69 | co_await xml::ignore_contents(e, /* until */ qname("trkseg")); |
| 70 | while (auto mseg = co_await allow_element( | 70 | while (auto mseg = co_await allow_element( |
| 71 | e, qname("trkseg"), xml::hohalo<parse_trkseg>())) | 71 | e, qname("trkseg"), xml::hohalo<parse_trkseg>())) |
| 72 | t.segments.push_back(std::move(*mseg)); | 72 | t.segments.push_back(std::move(*mseg)); |
| 73 | co_return std::move(t); | 73 | co_return std::move(t); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) | 76 | auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) |
| 77 | -> xml::parser<file> | 77 | -> xml::parser<file> |
| 78 | { | 78 | { |
| 79 | auto f = file{}; | 79 | auto f = file{}; |
| 80 | if (attrs.lookup(qname("version")) != "1.0"sv) | 80 | if (attrs.lookup(qname("version")) != "1.0"sv) |
| @@ -85,13 +85,13 @@ auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) | |||
| 85 | throw std::runtime_error{"expected GPX file to have creator"}; | 85 | throw std::runtime_error{"expected GPX file to have creator"}; |
| 86 | 86 | ||
| 87 | f.meta.name = co_await allow_element( | 87 | f.meta.name = co_await allow_element( |
| 88 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); | 88 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); |
| 89 | f.meta.desc = co_await allow_element( | 89 | f.meta.desc = co_await allow_element( |
| 90 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); | 90 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); |
| 91 | 91 | ||
| 92 | co_await xml::ignore_contents(e, /* until */ qname("trk")); | 92 | co_await xml::ignore_contents(e, /* until */ qname("trk")); |
| 93 | while (auto mtrk = | 93 | while (auto mtrk = |
| 94 | co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) | 94 | co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) |
| 95 | f.tracks.push_back(std::move(*mtrk)); | 95 | f.tracks.push_back(std::move(*mtrk)); |
| 96 | co_await xml::ignore_contents(e); | 96 | co_await xml::ignore_contents(e); |
| 97 | 97 | ||
| @@ -109,19 +109,19 @@ auto qname(std::string_view local) -> xml::qname_view | |||
| 109 | } | 109 | } |
| 110 | 110 | ||
| 111 | auto parse_metadata(xml::executor_ref e, xml::attribute_view) | 111 | auto parse_metadata(xml::executor_ref e, xml::attribute_view) |
| 112 | -> xml::parser<metadata> | 112 | -> xml::parser<metadata> |
| 113 | { | 113 | { |
| 114 | auto meta = metadata{}; | 114 | auto meta = metadata{}; |
| 115 | meta.name = co_await allow_element( | 115 | meta.name = co_await allow_element( |
| 116 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); | 116 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); |
| 117 | meta.desc = co_await allow_element( | 117 | meta.desc = co_await allow_element( |
| 118 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); | 118 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); |
| 119 | co_await xml::ignore_contents(e); | 119 | co_await xml::ignore_contents(e); |
| 120 | co_return meta; | 120 | co_return meta; |
| 121 | } | 121 | } |
| 122 | 122 | ||
| 123 | auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) | 123 | auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) |
| 124 | -> xml::parser<geo::point> | 124 | -> xml::parser<geo::point> |
| 125 | { | 125 | { |
| 126 | auto parse_xml_double = [](std::string_view sv) -> std::optional<double> | 126 | auto parse_xml_double = [](std::string_view sv) -> std::optional<double> |
| 127 | { return util::parse_double(sv, std::chars_format::fixed); }; | 127 | { return util::parse_double(sv, std::chars_format::fixed); }; |
| @@ -147,14 +147,14 @@ auto parse_wpt(xml::executor_ref e, xml::attribute_view attrs) | |||
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | auto parse_trkseg(xml::executor_ref e, xml::attribute_view) | 149 | auto parse_trkseg(xml::executor_ref e, xml::attribute_view) |
| 150 | -> xml::parser<track_segment> | 150 | -> xml::parser<track_segment> |
| 151 | { | 151 | { |
| 152 | auto s = track_segment{}; | 152 | auto s = track_segment{}; |
| 153 | while (auto mwpt = | 153 | while (auto mwpt = co_await allow_element( |
| 154 | co_await allow_element(e, qname("trkpt"), xml::hohalo<parse_wpt>())) | 154 | e, qname("trkpt"), xml::hohalo<parse_wpt>())) |
| 155 | bgeo::append(s.waypoints, *mwpt); | 155 | bgeo::append(s.waypoints, *mwpt); |
| 156 | co_await allow_element( | 156 | co_await allow_element( |
| 157 | e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); | 157 | e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); |
| 158 | co_return std::move(s); | 158 | co_return std::move(s); |
| 159 | } | 159 | } |
| 160 | 160 | ||
| @@ -162,20 +162,20 @@ auto parse_trk(xml::executor_ref e, xml::attribute_view) -> xml::parser<track> | |||
| 162 | { | 162 | { |
| 163 | auto t = track{}; | 163 | auto t = track{}; |
| 164 | t.name = co_await allow_element( | 164 | t.name = co_await allow_element( |
| 165 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); | 165 | e, qname("name"), xml::hohalo<xml::read_string_contents>()); |
| 166 | co_await allow_element( | 166 | co_await allow_element( |
| 167 | e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>()); | 167 | e, qname("cmt"), xml::hohalo<xml::ignore_element_contents>()); |
| 168 | t.desc = co_await allow_element( | 168 | t.desc = co_await allow_element( |
| 169 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); | 169 | e, qname("desc"), xml::hohalo<xml::read_string_contents>()); |
| 170 | co_await xml::ignore_contents(e, /* until */ qname("trkseg")); | 170 | co_await xml::ignore_contents(e, /* until */ qname("trkseg")); |
| 171 | while (auto mseg = co_await allow_element( | 171 | while (auto mseg = co_await allow_element( |
| 172 | e, qname("trkseg"), xml::hohalo<parse_trkseg>())) | 172 | e, qname("trkseg"), xml::hohalo<parse_trkseg>())) |
| 173 | t.segments.push_back(std::move(*mseg)); | 173 | t.segments.push_back(std::move(*mseg)); |
| 174 | co_return std::move(t); | 174 | co_return std::move(t); |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) | 177 | auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) |
| 178 | -> xml::parser<file> | 178 | -> xml::parser<file> |
| 179 | { | 179 | { |
| 180 | auto f = file{}; | 180 | auto f = file{}; |
| 181 | if (attrs.lookup(qname("version")) != "1.1"sv) | 181 | if (attrs.lookup(qname("version")) != "1.1"sv) |
| @@ -186,14 +186,14 @@ auto parse_gpx(xml::executor_ref e, xml::attribute_view attrs) | |||
| 186 | throw std::runtime_error{"expected GPX file to have creator"}; | 186 | throw std::runtime_error{"expected GPX file to have creator"}; |
| 187 | 187 | ||
| 188 | if (auto mmeta = co_await allow_element( | 188 | if (auto mmeta = co_await allow_element( |
| 189 | e, qname("metadata"), xml::hohalo<parse_metadata>())) | 189 | e, qname("metadata"), xml::hohalo<parse_metadata>())) |
| 190 | f.meta = *mmeta; | 190 | f.meta = *mmeta; |
| 191 | co_await xml::ignore_contents(e, /* until */ qname("trk")); | 191 | co_await xml::ignore_contents(e, /* until */ qname("trk")); |
| 192 | while (auto mtrk = | 192 | while (auto mtrk = |
| 193 | co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) | 193 | co_await allow_element(e, qname("trk"), xml::hohalo<parse_trk>())) |
| 194 | f.tracks.push_back(std::move(*mtrk)); | 194 | f.tracks.push_back(std::move(*mtrk)); |
| 195 | co_await allow_element( | 195 | co_await allow_element( |
| 196 | e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); | 196 | e, qname("extensions"), xml::hohalo<xml::ignore_element_contents>()); |
| 197 | 197 | ||
| 198 | co_return std::move(f); | 198 | co_return std::move(f); |
| 199 | } | 199 | } |
| @@ -205,14 +205,14 @@ auto parse_file(xml::executor_ref e) -> xml::parser<file> | |||
| 205 | auto decl = co_await expect_event<xml::xml_decl_event>(e); | 205 | auto decl = co_await expect_event<xml::xml_decl_event>(e); |
| 206 | if (decl.version != "1.0"sv) | 206 | if (decl.version != "1.0"sv) |
| 207 | throw std::runtime_error{std::format( | 207 | throw std::runtime_error{std::format( |
| 208 | "unsupported XML version, got {}", std::string_view{decl.version})}; | 208 | "unsupported XML version, got {}", std::string_view{decl.version})}; |
| 209 | if (decl.encoding != "UTF-8"sv) | 209 | if (decl.encoding != "UTF-8"sv) |
| 210 | throw std::runtime_error{"unsupported encoding"}; | 210 | throw std::runtime_error{"unsupported encoding"}; |
| 211 | if (auto mf = co_await allow_element( | 211 | if (auto mf = co_await allow_element( |
| 212 | e, v10::qname("gpx"), xml::hohalo<v10::parse_gpx>())) | 212 | e, v10::qname("gpx"), xml::hohalo<v10::parse_gpx>())) |
| 213 | co_return std::move(*mf); | 213 | co_return std::move(*mf); |
| 214 | if (auto mf = co_await allow_element( | 214 | if (auto mf = co_await allow_element( |
| 215 | e, v11::qname("gpx"), xml::hohalo<v11::parse_gpx>())) | 215 | e, v11::qname("gpx"), xml::hohalo<v11::parse_gpx>())) |
| 216 | co_return std::move(*mf); | 216 | co_return std::move(*mf); |
| 217 | throw std::runtime_error{"no supported GPX document found"}; | 217 | throw std::runtime_error{"no supported GPX document found"}; |
| 218 | } | 218 | } |
diff --git a/server/src/http_client.cppm b/server/src/http_client.cppm index 13dd60a..bea5384 100644 --- a/server/src/http_client.cppm +++ b/server/src/http_client.cppm | |||
| @@ -27,12 +27,12 @@ public: | |||
| 27 | { | 27 | { |
| 28 | sslc_.set_default_verify_paths(); | 28 | sslc_.set_default_verify_paths(); |
| 29 | sslc_.set_verify_mode( | 29 | sslc_.set_verify_mode( |
| 30 | net::ssl::verify_peer | net::ssl::verify_fail_if_no_peer_cert); | 30 | net::ssl::verify_peer | net::ssl::verify_fail_if_no_peer_cert); |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | template <class ReqBody> | 33 | template <class ReqBody> |
| 34 | auto do_request(bhttp::request<ReqBody>& req) | 34 | auto do_request(bhttp::request<ReqBody>& req) |
| 35 | -> bhttp::response<bhttp::dynamic_body> | 35 | -> bhttp::response<bhttp::dynamic_body> |
| 36 | { | 36 | { |
| 37 | auto stream = ssl::stream<beast::tcp_stream>{ioc_, sslc_}; | 37 | auto stream = ssl::stream<beast::tcp_stream>{ioc_, sslc_}; |
| 38 | 38 | ||
| @@ -40,7 +40,7 @@ public: | |||
| 40 | if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) | 40 | if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) |
| 41 | { | 41 | { |
| 42 | throw beast::system_error( | 42 | throw beast::system_error( |
| 43 | static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()); | 43 | static_cast<int>(::ERR_get_error()), net::error::get_ssl_category()); |
| 44 | } | 44 | } |
| 45 | stream.set_verify_callback(ssl::host_name_verification(host)); | 45 | stream.set_verify_callback(ssl::host_name_verification(host)); |
| 46 | auto const results = resolver_.resolve(host, "443"); | 46 | auto const results = resolver_.resolve(host, "443"); |
diff --git a/server/src/http_server.cppm b/server/src/http_server.cppm index 366037f..f5b4c3e 100644 --- a/server/src/http_server.cppm +++ b/server/src/http_server.cppm | |||
| @@ -41,7 +41,7 @@ private: | |||
| 41 | virtual ~impl_base() = default; | 41 | virtual ~impl_base() = default; |
| 42 | virtual auto header() -> bhttp::response_header<bhttp::fields>& = 0; | 42 | virtual auto header() -> bhttp::response_header<bhttp::fields>& = 0; |
| 43 | virtual auto header() const | 43 | virtual auto header() const |
| 44 | -> bhttp::response_header<bhttp::fields> const& = 0; | 44 | -> bhttp::response_header<bhttp::fields> const& = 0; |
| 45 | virtual auto is_done() const -> bool = 0; | 45 | virtual auto is_done() const -> bool = 0; |
| 46 | virtual auto prepare(beast::error_code&) -> const_buffers_type = 0; | 46 | virtual auto prepare(beast::error_code&) -> const_buffers_type = 0; |
| 47 | virtual auto consume(std::size_t n) -> void = 0; | 47 | virtual auto consume(std::size_t n) -> void = 0; |
| @@ -121,12 +121,12 @@ private: | |||
| 121 | auto keep_alive() const noexcept -> bool override | 121 | auto keep_alive() const noexcept -> bool override |
| 122 | { | 122 | { |
| 123 | return state_.visit( | 123 | return state_.visit( |
| 124 | util::overloaded{ | 124 | util::overloaded{ |
| 125 | [](bhttp::response<Body> const& rsp) -> bool | 125 | [](bhttp::response<Body> const& rsp) -> bool |
| 126 | { return rsp.keep_alive(); }, | 126 | { return rsp.keep_alive(); }, |
| 127 | [](bhttp::message_generator const& gen) -> bool | 127 | [](bhttp::message_generator const& gen) -> bool |
| 128 | { return gen.keep_alive(); }, | 128 | { return gen.keep_alive(); }, |
| 129 | }); | 129 | }); |
| 130 | } | 130 | } |
| 131 | }; | 131 | }; |
| 132 | 132 | ||
| @@ -164,15 +164,15 @@ using next_handler_t = std::function<auto(Ctx)->net::awaitable<presponse>>; | |||
| 164 | 164 | ||
| 165 | template <class OuterCtx, class InnerCtx> | 165 | template <class OuterCtx, class InnerCtx> |
| 166 | using middleware_t = | 166 | using middleware_t = |
| 167 | std::function<auto( | 167 | std::function<auto( |
| 168 | OuterCtx, bhttp::request_header<bhttp::fields>&, | 168 | OuterCtx, bhttp::request_header<bhttp::fields>&, |
| 169 | next_handler_t<InnerCtx>) | 169 | next_handler_t<InnerCtx>) |
| 170 | ->net::awaitable<presponse>>; | 170 | ->net::awaitable<presponse>>; |
| 171 | 171 | ||
| 172 | template <class Ctx> | 172 | template <class Ctx> |
| 173 | auto lax_cors_middleware( | 173 | auto lax_cors_middleware( |
| 174 | Ctx ctx, bhttp::request_header<bhttp::fields>& req_hdr, | 174 | Ctx ctx, bhttp::request_header<bhttp::fields>& req_hdr, |
| 175 | next_handler_t<Ctx> next) -> net::awaitable<presponse> | 175 | next_handler_t<Ctx> next) -> net::awaitable<presponse> |
| 176 | { | 176 | { |
| 177 | std::ignore = req_hdr; | 177 | std::ignore = req_hdr; |
| 178 | auto prersp = co_await next(ctx); | 178 | auto prersp = co_await next(ctx); |
| @@ -188,16 +188,16 @@ struct trace_id_ctx : InnerCtx | |||
| 188 | 188 | ||
| 189 | template <class OuterCtx> | 189 | template <class OuterCtx> |
| 190 | auto trace_id_middleware( | 190 | auto trace_id_middleware( |
| 191 | OuterCtx ctx0, bhttp::request_header<bhttp::fields>& req_hdr, | 191 | OuterCtx ctx0, bhttp::request_header<bhttp::fields>& req_hdr, |
| 192 | next_handler_t<trace_id_ctx<OuterCtx>> next) -> net::awaitable<presponse> | 192 | next_handler_t<trace_id_ctx<OuterCtx>> next) -> net::awaitable<presponse> |
| 193 | { | 193 | { |
| 194 | std::ignore = req_hdr; | 194 | std::ignore = req_hdr; |
| 195 | auto ctx = trace_id_ctx{std::move(ctx0)}; | 195 | auto ctx = trace_id_ctx{std::move(ctx0)}; |
| 196 | auto prersp = co_await next(std::move(ctx)); | 196 | auto prersp = co_await next(std::move(ctx)); |
| 197 | prersp.header().set( | 197 | prersp.header().set( |
| 198 | "X-Routemon-Trace-Id", std::string_view{ctx.trace_id.as_string()}); | 198 | "X-Routemon-Trace-Id", std::string_view{ctx.trace_id.as_string()}); |
| 199 | prersp.header().insert( | 199 | prersp.header().insert( |
| 200 | bhttp::field::access_control_expose_headers, "X-Routemon-Trace-Id"); | 200 | bhttp::field::access_control_expose_headers, "X-Routemon-Trace-Id"); |
| 201 | co_return std::move(prersp); | 201 | co_return std::move(prersp); |
| 202 | } | 202 | } |
| 203 | 203 | ||
| @@ -208,8 +208,8 @@ struct base_ctx | |||
| 208 | 208 | ||
| 209 | template <class Ctx> | 209 | template <class Ctx> |
| 210 | using basic_route_handler_fn_t = std::function< | 210 | using basic_route_handler_fn_t = std::function< |
| 211 | auto(Ctx, readable_request, std::vector<std::string> const& matches) | 211 | auto(Ctx, readable_request, std::vector<std::string> const& matches) |
| 212 | ->net::awaitable<presponse>>; | 212 | ->net::awaitable<presponse>>; |
| 213 | 213 | ||
| 214 | struct keep_alive | 214 | struct keep_alive |
| 215 | { | 215 | { |
| @@ -228,8 +228,8 @@ auto make_rsp(bhttp::status status, keep_alive ka) -> bhttp::response<Body> | |||
| 228 | } | 228 | } |
| 229 | 229 | ||
| 230 | auto problem_rsp( | 230 | auto problem_rsp( |
| 231 | base_ctx const& ctx, problem::details const& problem, keep_alive ka) | 231 | base_ctx const& ctx, problem::details const& problem, keep_alive ka) |
| 232 | -> presponse | 232 | -> presponse |
| 233 | { | 233 | { |
| 234 | auto rsp = make_rsp<bhttp::string_body>(problem.status, ka); | 234 | auto rsp = make_rsp<bhttp::string_body>(problem.status, ka); |
| 235 | rsp.set(bhttp::field::content_type, "application/problem+json"); | 235 | rsp.set(bhttp::field::content_type, "application/problem+json"); |
| @@ -244,17 +244,18 @@ struct preflight_response | |||
| 244 | std::vector<bhttp::field> allow_headers; | 244 | std::vector<bhttp::field> allow_headers; |
| 245 | }; | 245 | }; |
| 246 | auto make_preflight_rsp(preflight_response res, keep_alive ka) | 246 | auto make_preflight_rsp(preflight_response res, keep_alive ka) |
| 247 | -> bhttp::response<bhttp::empty_body> | 247 | -> bhttp::response<bhttp::empty_body> |
| 248 | { | 248 | { |
| 249 | auto rsp = make_rsp<bhttp::empty_body>(bhttp::status::no_content, ka); | 249 | auto rsp = make_rsp<bhttp::empty_body>(bhttp::status::no_content, ka); |
| 250 | auto allow_headers_str = res.allow_headers | 250 | auto allow_headers_str = res.allow_headers |
| 251 | | std::views::transform( | 251 | | std::views::transform( |
| 252 | [](auto const& field) -> std::string_view | 252 | [](auto const& field) -> std::string_view |
| 253 | { return bhttp::to_string(field); }) | 253 | { return bhttp::to_string(field); }) |
| 254 | | std::views::join_with(std::string_view{", "}) | 254 | | std::views::join_with(std::string_view{", "}) |
| 255 | | std::ranges::to<std::string>(); | 255 | | std::ranges::to<std::string>(); |
| 256 | rsp.set( | 256 | rsp.set( |
| 257 | bhttp::field::access_control_allow_methods, res.allow_methods.to_string()); | 257 | bhttp::field::access_control_allow_methods, |
| 258 | res.allow_methods.to_string()); | ||
| 258 | rsp.set(bhttp::field::access_control_allow_headers, allow_headers_str); | 259 | rsp.set(bhttp::field::access_control_allow_headers, allow_headers_str); |
| 259 | rsp.prepare_payload(); | 260 | rsp.prepare_payload(); |
| 260 | return rsp; | 261 | return rsp; |
| @@ -265,7 +266,7 @@ auto make_preflight_rsp(preflight_response res, keep_alive ka) | |||
| 265 | // anyway, but all context types should derive from base_ctx). | 266 | // anyway, but all context types should derive from base_ctx). |
| 266 | template <bhttp::concepts::body_reader Body> | 267 | template <bhttp::concepts::body_reader Body> |
| 267 | auto read_request(base_ctx const& ctx, readable_request&& r) | 268 | auto read_request(base_ctx const& ctx, readable_request&& r) |
| 268 | -> net::awaitable<std::expected<bhttp::request<Body>, presponse>> | 269 | -> net::awaitable<std::expected<bhttp::request<Body>, presponse>> |
| 269 | { | 270 | { |
| 270 | std::ignore = ctx; | 271 | std::ignore = ctx; |
| 271 | auto p = bhttp::request_parser<Body>{std::move(*r.p)}; | 272 | auto p = bhttp::request_parser<Body>{std::move(*r.p)}; |
| @@ -275,10 +276,11 @@ auto read_request(base_ctx const& ctx, readable_request&& r) | |||
| 275 | 276 | ||
| 276 | template <> | 277 | template <> |
| 277 | auto read_request<bhttp::empty_body>(base_ctx const& ctx, readable_request&& r) | 278 | auto read_request<bhttp::empty_body>(base_ctx const& ctx, readable_request&& r) |
| 278 | -> net::awaitable<std::expected<bhttp::request<bhttp::empty_body>, presponse>> | 279 | -> net::awaitable< |
| 280 | std::expected<bhttp::request<bhttp::empty_body>, presponse>> | ||
| 279 | { | 281 | { |
| 280 | auto [ec, _] = | 282 | auto [ec, _] = |
| 281 | co_await bhttp::async_read(*r.strm, *r.buf, *r.p, net::as_tuple); | 283 | co_await bhttp::async_read(*r.strm, *r.buf, *r.p, net::as_tuple); |
| 282 | if (ec == bhttp::error::unexpected_body) | 284 | if (ec == bhttp::error::unexpected_body) |
| 283 | { | 285 | { |
| 284 | auto tpl = problem::tpl{ | 286 | auto tpl = problem::tpl{ |
| @@ -287,7 +289,7 @@ auto read_request<bhttp::empty_body>(base_ctx const& ctx, readable_request&& r) | |||
| 287 | .type_uri = "https://routemon.fautchen.eu/problems/unexpected-body", | 289 | .type_uri = "https://routemon.fautchen.eu/problems/unexpected-body", |
| 288 | }; | 290 | }; |
| 289 | co_return std::unexpected{problem_rsp( | 291 | co_return std::unexpected{problem_rsp( |
| 290 | ctx, tpl.instantiate(), keep_alive{false})}; | 292 | ctx, tpl.instantiate(), keep_alive{false})}; |
| 291 | } | 293 | } |
| 292 | else if (ec) | 294 | else if (ec) |
| 293 | { | 295 | { |
| @@ -308,8 +310,8 @@ template <class Ctx> | |||
| 308 | []<class InnerCtx>(routed_ctx<InnerCtx> const&) {}(ctx); | 310 | []<class InnerCtx>(routed_ctx<InnerCtx> const&) {}(ctx); |
| 309 | } | 311 | } |
| 310 | auto default_options_handler( | 312 | auto default_options_handler( |
| 311 | Ctx const& ctx, readable_request r, std::vector<std::string> const&) | 313 | Ctx const& ctx, readable_request r, std::vector<std::string> const&) |
| 312 | -> net::awaitable<presponse> | 314 | -> net::awaitable<presponse> |
| 313 | { | 315 | { |
| 314 | auto mreq = co_await read_request<bhttp::empty_body>(ctx, std::move(r)); | 316 | auto mreq = co_await read_request<bhttp::empty_body>(ctx, std::move(r)); |
| 315 | if (!mreq) | 317 | if (!mreq) |
| @@ -319,18 +321,18 @@ auto default_options_handler( | |||
| 319 | { | 321 | { |
| 320 | // CORS preflight request | 322 | // CORS preflight request |
| 321 | co_return make_preflight_rsp( | 323 | co_return make_preflight_rsp( |
| 322 | preflight_response{ | 324 | preflight_response{ |
| 323 | // TODO: should access-control-allow-methods contain OPTIONS? | 325 | // TODO: should access-control-allow-methods contain OPTIONS? |
| 324 | .allow_methods = ctx.route_methods, | 326 | .allow_methods = ctx.route_methods, |
| 325 | .allow_headers = {bhttp::field::content_type}, | 327 | .allow_headers = {bhttp::field::content_type}, |
| 326 | }, | 328 | }, |
| 327 | keep_alive{mreq->keep_alive()}); | 329 | keep_alive{mreq->keep_alive()}); |
| 328 | } | 330 | } |
| 329 | else | 331 | else |
| 330 | { | 332 | { |
| 331 | // Normal OPTIONS request | 333 | // Normal OPTIONS request |
| 332 | auto rsp = make_rsp<bhttp::empty_body>( | 334 | auto rsp = make_rsp<bhttp::empty_body>( |
| 333 | bhttp::status::no_content, keep_alive{mreq->keep_alive()}); | 335 | bhttp::status::no_content, keep_alive{mreq->keep_alive()}); |
| 334 | rsp.set(bhttp::field::allow, ctx.route_methods.to_string()); | 336 | rsp.set(bhttp::field::allow, ctx.route_methods.to_string()); |
| 335 | rsp.prepare_payload(); | 337 | rsp.prepare_payload(); |
| 336 | co_return std::move(rsp); | 338 | co_return std::move(rsp); |
| @@ -338,7 +340,7 @@ auto default_options_handler( | |||
| 338 | } | 340 | } |
| 339 | 341 | ||
| 340 | auto global_options_handler(base_ctx const& ctx, readable_request r) | 342 | auto global_options_handler(base_ctx const& ctx, readable_request r) |
| 341 | -> net::awaitable<presponse> | 343 | -> net::awaitable<presponse> |
| 342 | { | 344 | { |
| 343 | // TODO: switch to "small (4KB) discarded" body type, similar to what Go | 345 | // TODO: switch to "small (4KB) discarded" body type, similar to what Go |
| 344 | // does? Same goes for default_options_handler? Not sure. | 346 | // does? Same goes for default_options_handler? Not sure. |
| @@ -347,45 +349,46 @@ auto global_options_handler(base_ctx const& ctx, readable_request r) | |||
| 347 | co_return std::move(res.error()); | 349 | co_return std::move(res.error()); |
| 348 | auto req = r.p->release(); | 350 | auto req = r.p->release(); |
| 349 | auto rsp = make_rsp<bhttp::empty_body>( | 351 | auto rsp = make_rsp<bhttp::empty_body>( |
| 350 | bhttp::status::no_content, keep_alive{req.keep_alive()}); | 352 | bhttp::status::no_content, keep_alive{req.keep_alive()}); |
| 351 | rsp.prepare_payload(); | 353 | rsp.prepare_payload(); |
| 352 | co_return std::move(rsp); | 354 | co_return std::move(rsp); |
| 353 | } | 355 | } |
| 354 | 356 | ||
| 355 | template <class Ctx> | 357 | template <class Ctx> |
| 356 | auto id_middleware( | 358 | auto id_middleware( |
| 357 | Ctx ctx, bhttp::request_header<bhttp::fields>&, next_handler_t<Ctx> next) | 359 | Ctx ctx, bhttp::request_header<bhttp::fields>&, next_handler_t<Ctx> next) |
| 358 | -> net::awaitable<presponse> | 360 | -> net::awaitable<presponse> |
| 359 | { | 361 | { |
| 360 | co_return co_await next(std::move(ctx)); | 362 | co_return co_await next(std::move(ctx)); |
| 361 | } | 363 | } |
| 362 | 364 | ||
| 363 | template <class A, class B, class C> | 365 | template <class A, class B, class C> |
| 364 | auto middleware_compose(middleware_t<A, B> ab, middleware_t<B, C> bc) | 366 | auto middleware_compose(middleware_t<A, B> ab, middleware_t<B, C> bc) |
| 365 | -> middleware_t<A, C> | 367 | -> middleware_t<A, C> |
| 366 | { | 368 | { |
| 367 | return [ab = std::move(ab), bc = std::move(bc)]( | 369 | return [ab = std::move(ab), bc = std::move(bc)]( |
| 368 | A a, bhttp::request_header<bhttp::fields>& header, | 370 | A a, bhttp::request_header<bhttp::fields>& header, |
| 369 | next_handler_t<C> next) -> net::awaitable<presponse> | 371 | next_handler_t<C> next) -> net::awaitable<presponse> |
| 370 | { | 372 | { |
| 371 | co_return co_await ab( | 373 | co_return co_await ab( |
| 372 | std::move(a), header, [&](B b) -> net::awaitable<presponse> | 374 | std::move(a), header, [&](B b) -> net::awaitable<presponse> |
| 373 | { co_return co_await bc(std::move(b), header, next); }); | 375 | { co_return co_await bc(std::move(b), header, next); }); |
| 374 | }; | 376 | }; |
| 375 | } | 377 | } |
| 376 | 378 | ||
| 377 | template <class A, class B> | 379 | template <class A, class B> |
| 378 | auto middleware_wrap_fn(middleware_t<A, B> ab, basic_route_handler_fn_t<B> fn) | 380 | auto middleware_wrap_fn(middleware_t<A, B> ab, basic_route_handler_fn_t<B> fn) |
| 379 | -> basic_route_handler_fn_t<A> | 381 | -> basic_route_handler_fn_t<A> |
| 380 | { | 382 | { |
| 381 | return [ab = std::move(ab), fn = std::move(fn)]( | 383 | return |
| 382 | A a_ctx, readable_request r, | 384 | [ab = std::move(ab), fn = std::move(fn)]( |
| 383 | std::vector<std::string> const& matches) -> net::awaitable<presponse> | 385 | A a_ctx, readable_request r, |
| 386 | std::vector<std::string> const& matches) -> net::awaitable<presponse> | ||
| 384 | { | 387 | { |
| 385 | co_return co_await ab( | 388 | co_return co_await ab( |
| 386 | std::move(a_ctx), r.p->get().base(), | 389 | std::move(a_ctx), r.p->get().base(), |
| 387 | [&](B b_ctx) -> net::awaitable<presponse> | 390 | [&](B b_ctx) -> net::awaitable<presponse> |
| 388 | { co_return co_await fn(std::move(b_ctx), r, matches); }); | 391 | { co_return co_await fn(std::move(b_ctx), r, matches); }); |
| 389 | }; | 392 | }; |
| 390 | } | 393 | } |
| 391 | 394 | ||
| @@ -445,7 +448,7 @@ struct handler_map | |||
| 445 | template <std::default_initializable U> | 448 | template <std::default_initializable U> |
| 446 | auto map(std::invocable<V const&> auto f) const -> handler_map<U> | 449 | auto map(std::invocable<V const&> auto f) const -> handler_map<U> |
| 447 | requires std::assignable_from< | 450 | requires std::assignable_from< |
| 448 | U&, std::invoke_result_t<decltype(f), V const&>> | 451 | U&, std::invoke_result_t<decltype(f), V const&>> |
| 449 | { | 452 | { |
| 450 | return { | 453 | return { |
| 451 | .options = static_cast<bool>(options) ? f(options) : U{}, | 454 | .options = static_cast<bool>(options) ? f(options) : U{}, |
| @@ -475,29 +478,29 @@ struct route_tree | |||
| 475 | 478 | ||
| 476 | template <class OuterCtx, class InnerCtx> | 479 | template <class OuterCtx, class InnerCtx> |
| 477 | auto middleware_wrap_tree( | 480 | auto middleware_wrap_tree( |
| 478 | middleware_t<OuterCtx, InnerCtx> mw, route_tree<InnerCtx> const& tree) | 481 | middleware_t<OuterCtx, InnerCtx> mw, route_tree<InnerCtx> const& tree) |
| 479 | -> route_tree<OuterCtx> | 482 | -> route_tree<OuterCtx> |
| 480 | { | 483 | { |
| 481 | auto new_leaves = tree.here.template map<basic_route_handler_fn_t<OuterCtx>>( | 484 | auto new_leaves = tree.here.template map<basic_route_handler_fn_t<OuterCtx>>( |
| 482 | std::bind_front(middleware_wrap_fn<OuterCtx, InnerCtx>, mw)); | 485 | std::bind_front(middleware_wrap_fn<OuterCtx, InnerCtx>, mw)); |
| 483 | auto new_sub = tree.sub.visit( | 486 | auto new_sub = tree.sub.visit( |
| 484 | util::overloaded{ | 487 | util::overloaded{ |
| 485 | [&mw](route_tree<InnerCtx>::named_subtrees const& subtrees) | 488 | [&mw](route_tree<InnerCtx>::named_subtrees const& subtrees) |
| 486 | -> decltype(route_tree<OuterCtx>::sub) | 489 | -> decltype(route_tree<OuterCtx>::sub) |
| 487 | { | 490 | { |
| 488 | auto new_subtrees = typename route_tree<OuterCtx>::named_subtrees{}; | 491 | auto new_subtrees = typename route_tree<OuterCtx>::named_subtrees{}; |
| 489 | for (auto [seg, subtree] : subtrees) | 492 | for (auto [seg, subtree] : subtrees) |
| 490 | new_subtrees[seg] = middleware_wrap_tree(mw, subtree); | 493 | new_subtrees[seg] = middleware_wrap_tree(mw, subtree); |
| 491 | return new_subtrees; | 494 | return new_subtrees; |
| 492 | }, | 495 | }, |
| 493 | [&mw](route_tree<InnerCtx>::wildcard_subtree const& subtree) | 496 | [&mw](route_tree<InnerCtx>::wildcard_subtree const& subtree) |
| 494 | -> decltype(route_tree<OuterCtx>::sub) | 497 | -> decltype(route_tree<OuterCtx>::sub) |
| 495 | { | 498 | { |
| 496 | return typename route_tree<OuterCtx>::wildcard_subtree{ | 499 | return typename route_tree<OuterCtx>::wildcard_subtree{ |
| 497 | middleware_wrap_tree(mw, *subtree) | 500 | middleware_wrap_tree(mw, *subtree) |
| 498 | }; | 501 | }; |
| 499 | }, | 502 | }, |
| 500 | }); | 503 | }); |
| 501 | return {.here = new_leaves, .sub = new_sub}; | 504 | return {.here = new_leaves, .sub = new_sub}; |
| 502 | } | 505 | } |
| 503 | 506 | ||
| @@ -506,21 +509,23 @@ concept match_arg = std::constructible_from<T, std::string const&>; | |||
| 506 | 509 | ||
| 507 | template <class Ctx, match_arg... MatchArgs> | 510 | template <class Ctx, match_arg... MatchArgs> |
| 508 | using route_handler_fn_t = std::function< | 511 | using route_handler_fn_t = std::function< |
| 509 | auto(Ctx, readable_request, MatchArgs...)->net::awaitable<presponse>>; | 512 | auto(Ctx, readable_request, MatchArgs...)->net::awaitable<presponse>>; |
| 510 | 513 | ||
| 511 | template <class Ctx, match_arg... MatchArgs> | 514 | template <class Ctx, match_arg... MatchArgs> |
| 512 | auto degen_route_handler(route_handler_fn_t<Ctx, MatchArgs...> fn) | 515 | auto degen_route_handler(route_handler_fn_t<Ctx, MatchArgs...> fn) |
| 513 | -> basic_route_handler_fn_t<Ctx> | 516 | -> basic_route_handler_fn_t<Ctx> |
| 514 | { | 517 | { |
| 515 | return [fn = std::move(fn)]( | 518 | return |
| 516 | Ctx ctx, readable_request r, | 519 | [fn = std::move(fn)]( |
| 517 | std::vector<std::string> const& matches) -> net::awaitable<presponse> | 520 | Ctx ctx, readable_request r, |
| 521 | std::vector<std::string> const& matches) -> net::awaitable<presponse> | ||
| 518 | { | 522 | { |
| 519 | if (sizeof...(MatchArgs) != matches.size()) | 523 | if (sizeof...(MatchArgs) != matches.size()) |
| 520 | throw std::runtime_error{"got unexpected amount of matches"}; | 524 | throw std::runtime_error{"got unexpected amount of matches"}; |
| 521 | auto it = matches.begin(); | 525 | auto it = matches.begin(); |
| 522 | co_return co_await fn( | 526 | co_return co_await fn( |
| 523 | std::move(ctx), r, MatchArgs{static_cast<std::string const&>(*it++)}...); | 527 | std::move(ctx), r, |
| 528 | MatchArgs{static_cast<std::string const&>(*it++)}...); | ||
| 524 | }; | 529 | }; |
| 525 | } | 530 | } |
| 526 | 531 | ||
| @@ -529,7 +534,7 @@ struct ctree : route_tree<Ctx> | |||
| 529 | { | 534 | { |
| 530 | template <class OuterCtx> | 535 | template <class OuterCtx> |
| 531 | auto wrap(middleware_t<OuterCtx, Ctx> mw) const | 536 | auto wrap(middleware_t<OuterCtx, Ctx> mw) const |
| 532 | -> ctree<OuterCtx, MatchArgs...> | 537 | -> ctree<OuterCtx, MatchArgs...> |
| 533 | { | 538 | { |
| 534 | return {middleware_wrap_tree(std::move(mw), *this)}; | 539 | return {middleware_wrap_tree(std::move(mw), *this)}; |
| 535 | } | 540 | } |
| @@ -541,24 +546,25 @@ struct dtree : handler_map<route_handler_fn_t<Ctx, MatchArgs...>> | |||
| 541 | [[nodiscard]] auto to_leaves() const -> typename route_tree<Ctx>::leaves | 546 | [[nodiscard]] auto to_leaves() const -> typename route_tree<Ctx>::leaves |
| 542 | { | 547 | { |
| 543 | auto here = this->template map<basic_route_handler_fn_t<Ctx>>( | 548 | auto here = this->template map<basic_route_handler_fn_t<Ctx>>( |
| 544 | degen_route_handler<Ctx, MatchArgs...>); | 549 | degen_route_handler<Ctx, MatchArgs...>); |
| 545 | if (!here.verbs().empty() && !static_cast<bool>(this->options)) | 550 | if (!here.verbs().empty() && !static_cast<bool>(this->options)) |
| 546 | here.options = default_options_handler<Ctx>; | 551 | here.options = default_options_handler<Ctx>; |
| 547 | return here; | 552 | return here; |
| 548 | } | 553 | } |
| 549 | 554 | ||
| 550 | [[nodiscard]] auto named_subtrees( | 555 | [[nodiscard]] auto named_subtrees( |
| 551 | std::initializer_list<std::pair<std::string, ctree<Ctx, MatchArgs...>>> | 556 | std::initializer_list<std::pair<std::string, ctree<Ctx, MatchArgs...>>> |
| 552 | subtrees) const -> ctree<Ctx, MatchArgs...> | 557 | subtrees) const -> ctree<Ctx, MatchArgs...> |
| 553 | { | 558 | { |
| 554 | auto sub = typename route_tree<Ctx>::named_subtrees{ | 559 | auto sub = typename route_tree<Ctx>::named_subtrees{ |
| 555 | std::from_range, subtrees | 560 | std::from_range, subtrees |
| 556 | | std::views::transform( | 561 | | std::views::transform( |
| 557 | [](auto const& p) | 562 | [](auto const& p) |
| 558 | { | 563 | { |
| 559 | return std::make_pair( | 564 | return std::make_pair( |
| 560 | p.first, static_cast<route_tree<Ctx>>(p.second)); | 565 | p.first, |
| 561 | }) | 566 | static_cast<route_tree<Ctx>>(p.second)); |
| 567 | }) | ||
| 562 | }; | 568 | }; |
| 563 | return {route_tree<Ctx>{.here = to_leaves(), .sub = sub}}; | 569 | return {route_tree<Ctx>{.here = to_leaves(), .sub = sub}}; |
| 564 | } | 570 | } |
| @@ -566,7 +572,7 @@ struct dtree : handler_map<route_handler_fn_t<Ctx, MatchArgs...>> | |||
| 566 | template <match_arg MatchArg> | 572 | template <match_arg MatchArg> |
| 567 | [[nodiscard]] auto | 573 | [[nodiscard]] auto |
| 568 | wildcard_subtree(ctree<Ctx, MatchArgs..., MatchArg> subtree) | 574 | wildcard_subtree(ctree<Ctx, MatchArgs..., MatchArg> subtree) |
| 569 | -> ctree<Ctx, MatchArgs...> | 575 | -> ctree<Ctx, MatchArgs...> |
| 570 | { | 576 | { |
| 571 | return {route_tree<Ctx>{ | 577 | return {route_tree<Ctx>{ |
| 572 | .here = to_leaves(), | 578 | .here = to_leaves(), |
| @@ -592,9 +598,9 @@ class server | |||
| 592 | 598 | ||
| 593 | public: | 599 | public: |
| 594 | explicit server( | 600 | explicit server( |
| 595 | log::logger const& l, locale::selector&& lsel, | 601 | log::logger const& l, locale::selector&& lsel, |
| 596 | middleware_t<base_ctx, PreRouteCtx> global_middleware, | 602 | middleware_t<base_ctx, PreRouteCtx> global_middleware, |
| 597 | route_tree<routed_ctx<PreRouteCtx>> routes) | 603 | route_tree<routed_ctx<PreRouteCtx>> routes) |
| 598 | : l_{l.sub("http_server")}, lsel_{std::move(lsel)}, | 604 | : l_{l.sub("http_server")}, lsel_{std::move(lsel)}, |
| 599 | global_middleware_{std::move(global_middleware)}, | 605 | global_middleware_{std::move(global_middleware)}, |
| 600 | routes_{std::move(routes)} | 606 | routes_{std::move(routes)} |
| @@ -604,36 +610,35 @@ public: | |||
| 604 | struct match_result | 610 | struct match_result |
| 605 | { | 611 | { |
| 606 | util::not_null< | 612 | util::not_null< |
| 607 | handler_map<basic_route_handler_fn_t<routed_ctx<PreRouteCtx>>> const*> | 613 | handler_map<basic_route_handler_fn_t<routed_ctx<PreRouteCtx>>> const*> |
| 608 | route_handlers; | 614 | route_handlers; |
| 609 | std::vector<std::string> wildcard_matches; | 615 | std::vector<std::string> wildcard_matches; |
| 610 | 616 | ||
| 611 | auto allowed_methods() const -> verb_set { return route_handlers->verbs(); } | 617 | auto allowed_methods() const -> verb_set { return route_handlers->verbs(); } |
| 612 | }; | 618 | }; |
| 613 | 619 | ||
| 614 | auto match(boost::urls::segments_view segments) const | 620 | auto match(boost::urls::segments_view segments) const |
| 615 | -> std::optional<match_result> | 621 | -> std::optional<match_result> |
| 616 | { | 622 | { |
| 617 | auto const* tree = &routes_; | 623 | auto const* tree = &routes_; |
| 618 | auto wildcard_matches = std::vector<std::string>{}; | 624 | auto wildcard_matches = std::vector<std::string>{}; |
| 619 | for (auto const& seg : segments) | 625 | for (auto const& seg : segments) |
| 620 | { | 626 | { |
| 621 | tree->sub.visit( | 627 | tree->sub.visit( |
| 622 | util::overloaded{ | 628 | util::overloaded{ |
| 623 | [&]( | 629 | [&](route_tree<routed_ctx<PreRouteCtx>>::named_subtrees const& |
| 624 | route_tree<routed_ctx<PreRouteCtx>>::named_subtrees const& subtrees) | 630 | subtrees) |
| 625 | { | 631 | { |
| 626 | auto it = subtrees.find(seg); | 632 | auto it = subtrees.find(seg); |
| 627 | tree = it == subtrees.end() ? nullptr : &it->second; | 633 | tree = it == subtrees.end() ? nullptr : &it->second; |
| 628 | }, | 634 | }, |
| 629 | [&]( | 635 | [&](route_tree<routed_ctx<PreRouteCtx>>::wildcard_subtree const& |
| 630 | route_tree<routed_ctx<PreRouteCtx>>::wildcard_subtree const& | 636 | wildcard_subtree) |
| 631 | wildcard_subtree) | 637 | { |
| 632 | { | 638 | wildcard_matches.push_back(seg); |
| 633 | wildcard_matches.push_back(seg); | 639 | tree = &*wildcard_subtree; |
| 634 | tree = &*wildcard_subtree; | 640 | }, |
| 635 | }, | 641 | }); |
| 636 | }); | ||
| 637 | if (!tree) | 642 | if (!tree) |
| 638 | return std::nullopt; | 643 | return std::nullopt; |
| 639 | } | 644 | } |
| @@ -646,7 +651,7 @@ public: | |||
| 646 | } | 651 | } |
| 647 | 652 | ||
| 648 | auto route_request(PreRouteCtx ctx, readable_request r) const | 653 | auto route_request(PreRouteCtx ctx, readable_request r) const |
| 649 | -> net::awaitable<presponse> | 654 | -> net::awaitable<presponse> |
| 650 | { | 655 | { |
| 651 | auto req_base = r.p->get().base(); | 656 | auto req_base = r.p->get().base(); |
| 652 | 657 | ||
| @@ -685,10 +690,10 @@ public: | |||
| 685 | if (!req_url.is_path_absolute()) | 690 | if (!req_url.is_path_absolute()) |
| 686 | { | 691 | { |
| 687 | auto problem = bad_request_tpl.instantiate().set_detail(translate( | 692 | auto problem = bad_request_tpl.instantiate().set_detail(translate( |
| 688 | "Path of normalized (RFC 3986, § 6) " | 693 | "Path of normalized (RFC 3986, § 6) " |
| 689 | "origin-form request-target (RFC " | 694 | "origin-form request-target (RFC " |
| 690 | "9112, § 3.2.1) should be " | 695 | "9112, § 3.2.1) should be " |
| 691 | "absolute")); | 696 | "absolute")); |
| 692 | co_return problem_rsp(ctx, problem, keep_alive{false}); | 697 | co_return problem_rsp(ctx, problem, keep_alive{false}); |
| 693 | } | 698 | } |
| 694 | 699 | ||
| @@ -719,7 +724,7 @@ public: | |||
| 719 | if (auto mhdl = mres->route_handlers->lookup(*mverb)) | 724 | if (auto mhdl = mres->route_handlers->lookup(*mverb)) |
| 720 | { | 725 | { |
| 721 | auto new_ctx = | 726 | auto new_ctx = |
| 722 | routed_ctx<PreRouteCtx>{std::move(ctx), mres->allowed_methods()}; | 727 | routed_ctx<PreRouteCtx>{std::move(ctx), mres->allowed_methods()}; |
| 723 | co_return co_await mhdl(std::move(new_ctx), r, mres->wildcard_matches); | 728 | co_return co_await mhdl(std::move(new_ctx), r, mres->wildcard_matches); |
| 724 | } | 729 | } |
| 725 | else | 730 | else |
| @@ -733,7 +738,7 @@ public: | |||
| 733 | }; | 738 | }; |
| 734 | auto rsp = problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); | 739 | auto rsp = problem_rsp(ctx, tpl.instantiate(), keep_alive{false}); |
| 735 | rsp.header().set( | 740 | rsp.header().set( |
| 736 | bhttp::field::allow, mres->allowed_methods().to_string()); | 741 | bhttp::field::allow, mres->allowed_methods().to_string()); |
| 737 | co_return std::move(rsp); | 742 | co_return std::move(rsp); |
| 738 | } | 743 | } |
| 739 | } | 744 | } |
| @@ -742,9 +747,9 @@ public: | |||
| 742 | // We do not accept any other request-target forms. | 747 | // We do not accept any other request-target forms. |
| 743 | 748 | ||
| 744 | auto problem = bad_request_tpl.instantiate().set_detail(translate( | 749 | auto problem = bad_request_tpl.instantiate().set_detail(translate( |
| 745 | "Invalid request-target, expected " | 750 | "Invalid request-target, expected " |
| 746 | "asterisk-form or origin-form " | 751 | "asterisk-form or origin-form " |
| 747 | "(see RFC 9112, § 3.2)")); | 752 | "(see RFC 9112, § 3.2)")); |
| 748 | co_return problem_rsp(ctx, problem, keep_alive{false}); | 753 | co_return problem_rsp(ctx, problem, keep_alive{false}); |
| 749 | } | 754 | } |
| 750 | } | 755 | } |
| @@ -756,8 +761,9 @@ public: | |||
| 756 | auto ctx0 = base_ctx{.locale = locale}; | 761 | auto ctx0 = base_ctx{.locale = locale}; |
| 757 | 762 | ||
| 758 | co_return co_await global_middleware_( | 763 | co_return co_await global_middleware_( |
| 759 | std::move(ctx0), header, [&](PreRouteCtx ctx) -> net::awaitable<presponse> | 764 | std::move(ctx0), header, |
| 760 | { co_return co_await route_request(std::move(ctx), std::move(r)); }); | 765 | [&](PreRouteCtx ctx) -> net::awaitable<presponse> |
| 766 | { co_return co_await route_request(std::move(ctx), std::move(r)); }); | ||
| 761 | } | 767 | } |
| 762 | 768 | ||
| 763 | auto do_session(beast::tcp_stream strm) -> net::awaitable<void> | 769 | auto do_session(beast::tcp_stream strm) -> net::awaitable<void> |
| @@ -769,7 +775,7 @@ public: | |||
| 769 | auto p0 = bhttp::request_parser<bhttp::empty_body>{}; | 775 | auto p0 = bhttp::request_parser<bhttp::empty_body>{}; |
| 770 | p0.body_limit(boost::none); | 776 | p0.body_limit(boost::none); |
| 771 | auto [ec, _] = | 777 | auto [ec, _] = |
| 772 | co_await bhttp::async_read_header(strm, buf, p0, net::as_tuple); | 778 | co_await bhttp::async_read_header(strm, buf, p0, net::as_tuple); |
| 773 | if (ec == bhttp::error::end_of_stream) | 779 | if (ec == bhttp::error::end_of_stream) |
| 774 | break; | 780 | break; |
| 775 | else if (ec) | 781 | else if (ec) |
| @@ -777,11 +783,11 @@ public: | |||
| 777 | 783 | ||
| 778 | auto http_version = p0.get().version(); | 784 | auto http_version = p0.get().version(); |
| 779 | auto&& rsp = co_await handle_request( | 785 | auto&& rsp = co_await handle_request( |
| 780 | readable_request{ | 786 | readable_request{ |
| 781 | .p = util::not_null{&p0}, | 787 | .p = util::not_null{&p0}, |
| 782 | .strm = util::not_null{&strm}, | 788 | .strm = util::not_null{&strm}, |
| 783 | .buf = util::not_null{&buf}, | 789 | .buf = util::not_null{&buf}, |
| 784 | }); | 790 | }); |
| 785 | rsp.header().version(http_version); | 791 | rsp.header().version(http_version); |
| 786 | bool keep_alive = rsp.keep_alive(); | 792 | bool keep_alive = rsp.keep_alive(); |
| 787 | co_await beast::async_write(strm, std::move(rsp)); | 793 | co_await beast::async_write(strm, std::move(rsp)); |
| @@ -800,27 +806,27 @@ public: | |||
| 800 | auto acceptor = tcp::acceptor{executor, endpoint}; | 806 | auto acceptor = tcp::acceptor{executor, endpoint}; |
| 801 | 807 | ||
| 802 | l_.with("endpoint", endpoint.address().to_string()) | 808 | l_.with("endpoint", endpoint.address().to_string()) |
| 803 | .with("port", std::to_string(endpoint.port())) | 809 | .with("port", std::to_string(endpoint.port())) |
| 804 | .info("Serving"); | 810 | .info("Serving"); |
| 805 | while (true) | 811 | while (true) |
| 806 | { | 812 | { |
| 807 | net::co_spawn( | 813 | net::co_spawn( |
| 808 | executor, | 814 | executor, |
| 809 | do_session(beast::tcp_stream{co_await acceptor.async_accept()}), | 815 | do_session(beast::tcp_stream{co_await acceptor.async_accept()}), |
| 810 | [this](std::exception_ptr e) | 816 | [this](std::exception_ptr e) |
| 811 | { | ||
| 812 | if (e) | ||
| 813 | { | 817 | { |
| 814 | try | 818 | if (e) |
| 815 | { | ||
| 816 | std::rethrow_exception(e); | ||
| 817 | } | ||
| 818 | catch (std::exception const& e) | ||
| 819 | { | 819 | { |
| 820 | l_.error("Error in session: {}", e.what()); | 820 | try |
| 821 | { | ||
| 822 | std::rethrow_exception(e); | ||
| 823 | } | ||
| 824 | catch (std::exception const& e) | ||
| 825 | { | ||
| 826 | l_.error("Error in session: {}", e.what()); | ||
| 827 | } | ||
| 821 | } | 828 | } |
| 822 | } | 829 | }); |
| 823 | }); | ||
| 824 | } | 830 | } |
| 825 | } | 831 | } |
| 826 | 832 | ||
| @@ -831,21 +837,21 @@ public: | |||
| 831 | 837 | ||
| 832 | // TODO: make exception handling as nice as in srv.cpp | 838 | // TODO: make exception handling as nice as in srv.cpp |
| 833 | net::co_spawn( | 839 | net::co_spawn( |
| 834 | ioc, do_listen(endpoint), | 840 | ioc, do_listen(endpoint), |
| 835 | [this](std::exception_ptr e) | 841 | [this](std::exception_ptr e) |
| 836 | { | ||
| 837 | if (e) | ||
| 838 | { | 842 | { |
| 839 | try | 843 | if (e) |
| 840 | { | ||
| 841 | std::rethrow_exception(e); | ||
| 842 | } | ||
| 843 | catch (std::exception const& e) | ||
| 844 | { | 844 | { |
| 845 | l_.error("Error: {}", e.what()); | 845 | try |
| 846 | { | ||
| 847 | std::rethrow_exception(e); | ||
| 848 | } | ||
| 849 | catch (std::exception const& e) | ||
| 850 | { | ||
| 851 | l_.error("Error: {}", e.what()); | ||
| 852 | } | ||
| 846 | } | 853 | } |
| 847 | } | 854 | }); |
| 848 | }); | ||
| 849 | } | 855 | } |
| 850 | }; | 856 | }; |
| 851 | 857 | ||
diff --git a/server/src/locale.cppm b/server/src/locale.cppm index 06607f0..da80f76 100644 --- a/server/src/locale.cppm +++ b/server/src/locale.cppm | |||
| @@ -50,11 +50,11 @@ struct icu_locale_hash | |||
| 50 | }; | 50 | }; |
| 51 | 51 | ||
| 52 | using icu_locale_priority_map = | 52 | using icu_locale_priority_map = |
| 53 | std::unordered_map<icu::Locale, locale_priority, icu_locale_hash>; | 53 | std::unordered_map<icu::Locale, locale_priority, icu_locale_hash>; |
| 54 | using icu_priority_locale = std::pair<icu::Locale, locale_priority>; | 54 | using icu_priority_locale = std::pair<icu::Locale, locale_priority>; |
| 55 | 55 | ||
| 56 | auto operator<(icu_priority_locale const& lhs, icu_priority_locale const& rhs) | 56 | auto operator<(icu_priority_locale const& lhs, icu_priority_locale const& rhs) |
| 57 | -> bool | 57 | -> bool |
| 58 | { | 58 | { |
| 59 | return lhs.second < rhs.second; | 59 | return lhs.second < rhs.second; |
| 60 | } | 60 | } |
| @@ -66,7 +66,7 @@ class icu_priority_locale_vec_iterator : public icu::Locale::Iterator | |||
| 66 | 66 | ||
| 67 | public: | 67 | public: |
| 68 | explicit icu_priority_locale_vec_iterator( | 68 | explicit icu_priority_locale_vec_iterator( |
| 69 | std::vector<icu_priority_locale>&& ls) | 69 | std::vector<icu_priority_locale>&& ls) |
| 70 | : ls_(std::move(ls)) | 70 | : ls_(std::move(ls)) |
| 71 | { | 71 | { |
| 72 | } | 72 | } |
| @@ -80,8 +80,9 @@ public: | |||
| 80 | 80 | ||
| 81 | export template <class T> | 81 | export template <class T> |
| 82 | concept locale_input_range = | 82 | concept locale_input_range = |
| 83 | std::ranges::input_range<T> | 83 | std::ranges::input_range<T> |
| 84 | && std::same_as<std::locale const&, std::ranges::range_const_reference_t<T>>; | 84 | && std::same_as< |
| 85 | std::locale const&, std::ranges::range_const_reference_t<T>>; | ||
| 85 | 86 | ||
| 86 | // Helps select a locale based on the Accept-Language header in an | 87 | // Helps select a locale based on the Accept-Language header in an |
| 87 | // HTTP request. | 88 | // HTTP request. |
| @@ -92,15 +93,15 @@ export class selector | |||
| 92 | std::shared_ptr<blocale::generator const> lgen_; | 93 | std::shared_ptr<blocale::generator const> lgen_; |
| 93 | 94 | ||
| 94 | auto make_matcher( | 95 | auto make_matcher( |
| 95 | locale_input_range auto supported_locales, std::locale default_locale) | 96 | locale_input_range auto supported_locales, std::locale default_locale) |
| 96 | { | 97 | { |
| 97 | auto builder = icu::LocaleMatcher::Builder{}; | 98 | auto builder = icu::LocaleMatcher::Builder{}; |
| 98 | for (auto const& supported_locale : supported_locales) | 99 | for (auto const& supported_locale : supported_locales) |
| 99 | { | 100 | { |
| 100 | auto const& supported_locale_info = | 101 | auto const& supported_locale_info = |
| 101 | std::use_facet<blocale::info>(supported_locale); | 102 | std::use_facet<blocale::info>(supported_locale); |
| 102 | auto supported_icu_locale = | 103 | auto supported_icu_locale = |
| 103 | icu::Locale{supported_locale_info.name().c_str()}; | 104 | icu::Locale{supported_locale_info.name().c_str()}; |
| 104 | if (supported_icu_locale.isBogus()) | 105 | if (supported_icu_locale.isBogus()) |
| 105 | throw std::runtime_error{ | 106 | throw std::runtime_error{ |
| 106 | "supported locale gives rise to bogus ICU locale" | 107 | "supported locale gives rise to bogus ICU locale" |
| @@ -108,7 +109,7 @@ export class selector | |||
| 108 | builder.addSupportedLocale(supported_icu_locale); | 109 | builder.addSupportedLocale(supported_icu_locale); |
| 109 | } | 110 | } |
| 110 | auto const& default_locale_info = | 111 | auto const& default_locale_info = |
| 111 | std::use_facet<blocale::info>(default_locale); | 112 | std::use_facet<blocale::info>(default_locale); |
| 112 | auto default_icu_locale = icu::Locale{default_locale_info.name().c_str()}; | 113 | auto default_icu_locale = icu::Locale{default_locale_info.name().c_str()}; |
| 113 | if (default_icu_locale.isBogus()) | 114 | if (default_icu_locale.isBogus()) |
| 114 | throw std::runtime_error{"default locale gives rise to bogus ICU locale"}; | 115 | throw std::runtime_error{"default locale gives rise to bogus ICU locale"}; |
| @@ -170,7 +171,7 @@ export class selector | |||
| 170 | while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw) | 171 | while (kw = keywords->next(&kw_len, ec), !U_FAILURE(ec) && kw) |
| 171 | { | 172 | { |
| 172 | auto value = | 173 | auto value = |
| 173 | l.getKeywordValue<std::string>(icu::StringPiece(kw, kw_len), ec); | 174 | l.getKeywordValue<std::string>(icu::StringPiece(kw, kw_len), ec); |
| 174 | if (!added_at) | 175 | if (!added_at) |
| 175 | { | 176 | { |
| 176 | posix_name += "@"; | 177 | posix_name += "@"; |
| @@ -195,8 +196,8 @@ public: | |||
| 195 | // Note: lgen must live at least as long as the selector constructed here! | 196 | // Note: lgen must live at least as long as the selector constructed here! |
| 196 | // It is unfortunately not possible to copy/move a blocale::generator. | 197 | // It is unfortunately not possible to copy/move a blocale::generator. |
| 197 | explicit selector( | 198 | explicit selector( |
| 198 | locale_input_range auto locales, std::locale default_, | 199 | locale_input_range auto locales, std::locale default_, |
| 199 | std::shared_ptr<blocale::generator const> lgen) | 200 | std::shared_ptr<blocale::generator const> lgen) |
| 200 | : default_{default_}, matcher_{make_matcher(locales, default_)}, lgen_{lgen} | 201 | : default_{default_}, matcher_{make_matcher(locales, default_)}, lgen_{lgen} |
| 201 | { | 202 | { |
| 202 | } | 203 | } |
| @@ -211,7 +212,7 @@ public: | |||
| 211 | accept_language | std::views::split(","sv) | std::views::enumerate) | 212 | accept_language | std::views::split(","sv) | std::views::enumerate) |
| 212 | { | 213 | { |
| 213 | auto [lang_range_ut, mweight_ut] = | 214 | auto [lang_range_ut, mweight_ut] = |
| 214 | util::split_on(std::string_view{lang_prio}, ';'); | 215 | util::split_on(std::string_view{lang_prio}, ';'); |
| 215 | auto lang_range_str = trim_ows(lang_range_ut); | 216 | auto lang_range_str = trim_ows(lang_range_ut); |
| 216 | auto mweight_str = mweight_ut.transform(trim_ows); | 217 | auto mweight_str = mweight_ut.transform(trim_ows); |
| 217 | if (lang_range_str == "*") | 218 | if (lang_range_str == "*") |
| @@ -227,7 +228,7 @@ public: | |||
| 227 | { | 228 | { |
| 228 | auto weight_str = mweight_str->substr(2, 4); | 229 | auto weight_str = mweight_str->substr(2, 4); |
| 229 | if (auto mweight = | 230 | if (auto mweight = |
| 230 | util::parse_float(weight_str, std::chars_format::fixed); | 231 | util::parse_float(weight_str, std::chars_format::fixed); |
| 231 | mweight && 0.0f < *mweight && *mweight < 1.0f) | 232 | mweight && 0.0f < *mweight && *mweight < 1.0f) |
| 232 | { | 233 | { |
| 233 | weight = *mweight; | 234 | weight = *mweight; |
| @@ -248,7 +249,7 @@ public: | |||
| 248 | } | 249 | } |
| 249 | 250 | ||
| 250 | auto desired_locales = | 251 | auto desired_locales = |
| 251 | std::vector<icu_priority_locale>{dlpm.begin(), dlpm.end()}; | 252 | std::vector<icu_priority_locale>{dlpm.begin(), dlpm.end()}; |
| 252 | std::sort(desired_locales.begin(), desired_locales.end()); | 253 | std::sort(desired_locales.begin(), desired_locales.end()); |
| 253 | auto it = icu_priority_locale_vec_iterator{std::move(desired_locales)}; | 254 | auto it = icu_priority_locale_vec_iterator{std::move(desired_locales)}; |
| 254 | auto ec = UErrorCode::U_ZERO_ERROR; | 255 | auto ec = UErrorCode::U_ZERO_ERROR; |
| @@ -267,7 +268,7 @@ export auto to_bcp47_lang_tag(std::locale locale) -> std::optional<std::string> | |||
| 267 | auto const& locale_info = std::use_facet<blocale::info>(locale); | 268 | auto const& locale_info = std::use_facet<blocale::info>(locale); |
| 268 | auto ec = UErrorCode::U_ZERO_ERROR; | 269 | auto ec = UErrorCode::U_ZERO_ERROR; |
| 269 | auto bcp47_lang_tag = | 270 | auto bcp47_lang_tag = |
| 270 | icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec); | 271 | icu::Locale{locale_info.name().c_str()}.toLanguageTag<std::string>(ec); |
| 271 | if (U_FAILURE(ec)) | 272 | if (U_FAILURE(ec)) |
| 272 | return std::nullopt; | 273 | return std::nullopt; |
| 273 | return bcp47_lang_tag; | 274 | return bcp47_lang_tag; |
diff --git a/server/src/log.cppm b/server/src/log.cppm index 7c42144..97552b6 100644 --- a/server/src/log.cppm +++ b/server/src/log.cppm | |||
| @@ -97,12 +97,12 @@ export class logger | |||
| 97 | { | 97 | { |
| 98 | if (sink_->level() <= lvl) | 98 | if (sink_->level() <= lvl) |
| 99 | sink_->write( | 99 | sink_->write( |
| 100 | sink::tmp_message{ | 100 | sink::tmp_message{ |
| 101 | .lvl = lvl, | 101 | .lvl = lvl, |
| 102 | .component = component_, | 102 | .component = component_, |
| 103 | .txt = std::vformat(fmt, args), | 103 | .txt = std::vformat(fmt, args), |
| 104 | .attrs = attrs_, | 104 | .attrs = attrs_, |
| 105 | }); | 105 | }); |
| 106 | return *this; | 106 | return *this; |
| 107 | } | 107 | } |
| 108 | 108 | ||
| @@ -138,7 +138,7 @@ public: | |||
| 138 | } | 138 | } |
| 139 | 139 | ||
| 140 | [[nodiscard]] auto with(std::string const& k, std::string_view v) const | 140 | [[nodiscard]] auto with(std::string const& k, std::string_view v) const |
| 141 | -> logger | 141 | -> logger |
| 142 | { | 142 | { |
| 143 | return with(k, std::string{v}); | 143 | return with(k, std::string{v}); |
| 144 | } | 144 | } |
diff --git a/server/src/main.cpp b/server/src/main.cpp index 2957808..3f8cb11 100644 --- a/server/src/main.cpp +++ b/server/src/main.cpp | |||
| @@ -21,9 +21,9 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 21 | if (args.size() != 2) | 21 | if (args.size() != 2) |
| 22 | { | 22 | { |
| 23 | l.error( | 23 | l.error( |
| 24 | "Fatal: expected exactly one argument (the configuration file " | 24 | "Fatal: expected exactly one argument (the configuration file " |
| 25 | "location), got {}", | 25 | "location), got {}", |
| 26 | args.size() - 1); | 26 | args.size() - 1); |
| 27 | return exit_status::bad_usage; | 27 | return exit_status::bad_usage; |
| 28 | } | 28 | } |
| 29 | auto const* config_filename = args[1]; | 29 | auto const* config_filename = args[1]; |
| @@ -48,7 +48,7 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 48 | catch (std::exception const& e) | 48 | catch (std::exception const& e) |
| 49 | { | 49 | { |
| 50 | l.with("filename", std::string_view{config_filename}) | 50 | l.with("filename", std::string_view{config_filename}) |
| 51 | .error("Failed to load configuration: {}", e.what()); | 51 | .error("Failed to load configuration: {}", e.what()); |
| 52 | return exit_status::failure; | 52 | return exit_status::failure; |
| 53 | } | 53 | } |
| 54 | sink->set_level(config.logger.level); | 54 | sink->set_level(config.logger.level); |
| @@ -68,19 +68,19 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 68 | catch (std::exception const& e) | 68 | catch (std::exception const& e) |
| 69 | { | 69 | { |
| 70 | l.with("filename", config.database.sqlite3_filename) | 70 | l.with("filename", config.database.sqlite3_filename) |
| 71 | .error("Failed to open database: {}", e.what()); | 71 | .error("Failed to open database: {}", e.what()); |
| 72 | return exit_status::failure; | 72 | return exit_status::failure; |
| 73 | } | 73 | } |
| 74 | 74 | ||
| 75 | l.with("filename", config.situations.datex2_filename) | 75 | l.with("filename", config.situations.datex2_filename) |
| 76 | .info("Loading situations"); | 76 | .info("Loading situations"); |
| 77 | auto const before_load = chrono::steady_clock::now(); | 77 | auto const before_load = chrono::steady_clock::now(); |
| 78 | auto d2loader = routemon::datex2::loader{}; | 78 | auto d2loader = routemon::datex2::loader{}; |
| 79 | auto pub = routemon::datex2::situation_publication{}; | 79 | auto pub = routemon::datex2::situation_publication{}; |
| 80 | try | 80 | try |
| 81 | { | 81 | { |
| 82 | pub = | 82 | pub = |
| 83 | d2loader.load_situation_publication(config.situations.datex2_filename); | 83 | d2loader.load_situation_publication(config.situations.datex2_filename); |
| 84 | } | 84 | } |
| 85 | catch (std::exception const& e) | 85 | catch (std::exception const& e) |
| 86 | { | 86 | { |
| @@ -91,9 +91,9 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 91 | { | 91 | { |
| 92 | auto const& warns = d2loader.warnings(); | 92 | auto const& warns = d2loader.warnings(); |
| 93 | l.warn( | 93 | l.warn( |
| 94 | "Encountered {} unique warnings while loading DATEX II situations " | 94 | "Encountered {} unique warnings while loading DATEX II situations " |
| 95 | "publication", | 95 | "publication", |
| 96 | warns.size()); | 96 | warns.size()); |
| 97 | auto i = 0uz; | 97 | auto i = 0uz; |
| 98 | for (auto it = warns.begin(); it != warns.end(); | 98 | for (auto it = warns.begin(); it != warns.end(); |
| 99 | it = warns.upper_bound(*it)) | 99 | it = warns.upper_bound(*it)) |
| @@ -108,12 +108,12 @@ auto real_main(std::span<char const*> args) -> exit_status | |||
| 108 | malloc_trim(0); | 108 | malloc_trim(0); |
| 109 | auto const after_load = chrono::steady_clock::now(); | 109 | auto const after_load = chrono::steady_clock::now(); |
| 110 | auto const dur_load = | 110 | auto const dur_load = |
| 111 | chrono::duration_cast<chrono::milliseconds>(after_load - before_load); | 111 | chrono::duration_cast<chrono::milliseconds>(after_load - before_load); |
| 112 | l.info("Loading situations finished in {}", dur_load); | 112 | l.info("Loading situations finished in {}", dur_load); |
| 113 | 113 | ||
| 114 | auto handler = routemon::api::handler{l, std::move(pub)}; | 114 | auto handler = routemon::api::handler{l, std::move(pub)}; |
| 115 | auto http_server = | 115 | auto http_server = |
| 116 | routemon::srv::server{l, std::move(lsel), std::move(handler)}; | 116 | routemon::srv::server{l, std::move(lsel), std::move(handler)}; |
| 117 | http_server.spawn(ioc); | 117 | http_server.spawn(ioc); |
| 118 | ioc.run(); | 118 | ioc.run(); |
| 119 | 119 | ||
| @@ -129,7 +129,9 @@ auto main(int argc, char* argv[]) -> int | |||
| 129 | return EXIT_FAILURE; | 129 | return EXIT_FAILURE; |
| 130 | } | 130 | } |
| 131 | auto res = real_main( | 131 | auto res = real_main( |
| 132 | std::span{const_cast<char const**>(argv), static_cast<std::size_t>(argc)}); | 132 | std::span{ |
| 133 | const_cast<char const**>(argv), static_cast<std::size_t>(argc) | ||
| 134 | }); | ||
| 133 | switch (res) | 135 | switch (res) |
| 134 | { | 136 | { |
| 135 | case exit_status::failure: | 137 | case exit_status::failure: |
diff --git a/server/src/problem.cppm b/server/src/problem.cppm index a72379a..37e6257 100644 --- a/server/src/problem.cppm +++ b/server/src/problem.cppm | |||
| @@ -41,8 +41,8 @@ export struct details | |||
| 41 | }; | 41 | }; |
| 42 | 42 | ||
| 43 | export auto tag_invoke( | 43 | export auto tag_invoke( |
| 44 | json::value_from_tag, json::value& jv, details const& details, | 44 | json::value_from_tag, json::value& jv, details const& details, |
| 45 | std::locale locale) -> void | 45 | std::locale locale) -> void |
| 46 | { | 46 | { |
| 47 | auto obj = json::object{ | 47 | auto obj = json::object{ |
| 48 | {"type", details.type_uri}, | 48 | {"type", details.type_uri}, |
diff --git a/server/src/req_ctx.cppm b/server/src/req_ctx.cppm index 464f2ff..4a64d7d 100644 --- a/server/src/req_ctx.cppm +++ b/server/src/req_ctx.cppm | |||
| @@ -20,8 +20,8 @@ export class req_ctx | |||
| 20 | 20 | ||
| 21 | public: | 21 | public: |
| 22 | explicit req_ctx( | 22 | explicit req_ctx( |
| 23 | trace::id tid, std::locale locale, http::verb_set route_verbs, | 23 | trace::id tid, std::locale locale, http::verb_set route_verbs, |
| 24 | bool keep_alive, bhttp::request_header<bhttp::fields> const& req_header) | 24 | bool keep_alive, bhttp::request_header<bhttp::fields> const& req_header) |
| 25 | : tid_{tid}, locale_{locale}, route_verbs_{route_verbs}, | 25 | : tid_{tid}, locale_{locale}, route_verbs_{route_verbs}, |
| 26 | keep_alive_{keep_alive}, req_header_{req_header} | 26 | keep_alive_{keep_alive}, req_header_{req_header} |
| 27 | { | 27 | { |
| @@ -29,8 +29,8 @@ public: | |||
| 29 | 29 | ||
| 30 | template <typename ReqBody> | 30 | template <typename ReqBody> |
| 31 | explicit req_ctx( | 31 | explicit req_ctx( |
| 32 | trace::id tid, std::locale locale, http::verb_set route_verbs, | 32 | trace::id tid, std::locale locale, http::verb_set route_verbs, |
| 33 | bhttp::request<ReqBody> const& req) | 33 | bhttp::request<ReqBody> const& req) |
| 34 | : req_ctx{tid, locale, route_verbs, req.keep_alive(), req.base()} | 34 | : req_ctx{tid, locale, route_verbs, req.keep_alive(), req.base()} |
| 35 | { | 35 | { |
| 36 | } | 36 | } |
diff --git a/server/src/rwgps.cppm b/server/src/rwgps.cppm index f2f1681..3b01e6c 100644 --- a/server/src/rwgps.cppm +++ b/server/src/rwgps.cppm | |||
| @@ -46,7 +46,7 @@ struct get_routes_response | |||
| 46 | }; | 46 | }; |
| 47 | 47 | ||
| 48 | auto tag_invoke(json::value_to_tag<route_summary> const&, json::value const& jv) | 48 | auto tag_invoke(json::value_to_tag<route_summary> const&, json::value const& jv) |
| 49 | -> route_summary | 49 | -> route_summary |
| 50 | { | 50 | { |
| 51 | return { | 51 | return { |
| 52 | .id = json::value_to<std::int64_t>(jv.at("id")), | 52 | .id = json::value_to<std::int64_t>(jv.at("id")), |
| @@ -58,20 +58,20 @@ auto tag_invoke(json::value_to_tag<route_summary> const&, json::value const& jv) | |||
| 58 | } | 58 | } |
| 59 | 59 | ||
| 60 | auto tag_invoke(json::value_to_tag<pagination> const&, json::value const& jv) | 60 | auto tag_invoke(json::value_to_tag<pagination> const&, json::value const& jv) |
| 61 | -> pagination | 61 | -> pagination |
| 62 | { | 62 | { |
| 63 | return { | 63 | return { |
| 64 | .record_count = json::value_to<std::size_t>(jv.at("record_count")), | 64 | .record_count = json::value_to<std::size_t>(jv.at("record_count")), |
| 65 | .page_count = json::value_to<std::size_t>(jv.at("page_count")), | 65 | .page_count = json::value_to<std::size_t>(jv.at("page_count")), |
| 66 | .page_size = json::value_to<std::size_t>(jv.at("page_size")), | 66 | .page_size = json::value_to<std::size_t>(jv.at("page_size")), |
| 67 | .next_page_url = | 67 | .next_page_url = |
| 68 | json::value_to<std::optional<std::string>>(jv.at("next_page_url")), | 68 | json::value_to<std::optional<std::string>>(jv.at("next_page_url")), |
| 69 | }; | 69 | }; |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | auto tag_invoke( | 72 | auto tag_invoke( |
| 73 | json::value_to_tag<get_routes_meta> const&, json::value const& jv) | 73 | json::value_to_tag<get_routes_meta> const&, json::value const& jv) |
| 74 | -> get_routes_meta | 74 | -> get_routes_meta |
| 75 | { | 75 | { |
| 76 | return { | 76 | return { |
| 77 | .pagination = json::value_to<pagination>(jv.at("pagination")), | 77 | .pagination = json::value_to<pagination>(jv.at("pagination")), |
| @@ -79,8 +79,8 @@ auto tag_invoke( | |||
| 79 | } | 79 | } |
| 80 | 80 | ||
| 81 | auto tag_invoke( | 81 | auto tag_invoke( |
| 82 | json::value_to_tag<get_routes_response> const&, json::value const& jv) | 82 | json::value_to_tag<get_routes_response> const&, json::value const& jv) |
| 83 | -> get_routes_response | 83 | -> get_routes_response |
| 84 | { | 84 | { |
| 85 | return { | 85 | return { |
| 86 | .routes = json::value_to<std::vector<route_summary>>(jv.at("routes")), | 86 | .routes = json::value_to<std::vector<route_summary>>(jv.at("routes")), |
| @@ -89,7 +89,7 @@ auto tag_invoke( | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | auto json_value_to_get_routes_response(json::value const& jv) | 91 | auto json_value_to_get_routes_response(json::value const& jv) |
| 92 | -> get_routes_response | 92 | -> get_routes_response |
| 93 | { | 93 | { |
| 94 | return json::value_to<get_routes_response>(jv); | 94 | return json::value_to<get_routes_response>(jv); |
| 95 | } | 95 | } |
| @@ -127,8 +127,8 @@ export class client | |||
| 127 | 127 | ||
| 128 | public: | 128 | public: |
| 129 | explicit client( | 129 | explicit client( |
| 130 | net::io_context& ioc, log::logger const& l, std::string api_key, | 130 | net::io_context& ioc, log::logger const& l, std::string api_key, |
| 131 | std::string auth_token) | 131 | std::string auth_token) |
| 132 | : l_{l.sub("rwgps-client")}, hc_{ioc}, api_key_{std::move(api_key)}, | 132 | : l_{l.sub("rwgps-client")}, hc_{ioc}, api_key_{std::move(api_key)}, |
| 133 | auth_token_{std::move(auth_token)} | 133 | auth_token_{std::move(auth_token)} |
| 134 | { | 134 | { |
diff --git a/server/src/sqlite3.cppm b/server/src/sqlite3.cppm index 45366b8..4623cdc 100644 --- a/server/src/sqlite3.cppm +++ b/server/src/sqlite3.cppm | |||
| @@ -18,7 +18,7 @@ class mutex_guard | |||
| 18 | 18 | ||
| 19 | friend auto | 19 | friend auto |
| 20 | do_guarded(::sqlite3_mutex* mut, std::invocable<mutex_guard const&> auto f) | 20 | do_guarded(::sqlite3_mutex* mut, std::invocable<mutex_guard const&> auto f) |
| 21 | -> decltype(f(std::declval<mutex_guard const&>())); | 21 | -> decltype(f(std::declval<mutex_guard const&>())); |
| 22 | 22 | ||
| 23 | public: | 23 | public: |
| 24 | mutex_guard(mutex_guard const&) = delete; | 24 | mutex_guard(mutex_guard const&) = delete; |
| @@ -29,13 +29,13 @@ private: | |||
| 29 | }; | 29 | }; |
| 30 | 30 | ||
| 31 | auto do_guarded(::sqlite3_mutex* mut, std::invocable<mutex_guard const&> auto f) | 31 | auto do_guarded(::sqlite3_mutex* mut, std::invocable<mutex_guard const&> auto f) |
| 32 | -> decltype(f(std::declval<mutex_guard const&>())) | 32 | -> decltype(f(std::declval<mutex_guard const&>())) |
| 33 | { | 33 | { |
| 34 | return f(mutex_guard{mut}); | 34 | return f(mutex_guard{mut}); |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | auto do_guarded(::sqlite3* dbc, std::invocable<mutex_guard const&> auto f) | 37 | auto do_guarded(::sqlite3* dbc, std::invocable<mutex_guard const&> auto f) |
| 38 | -> decltype(f(std::declval<mutex_guard const&>())) | 38 | -> decltype(f(std::declval<mutex_guard const&>())) |
| 39 | { | 39 | { |
| 40 | return do_guarded(::sqlite3_db_mutex(dbc), f); | 40 | return do_guarded(::sqlite3_db_mutex(dbc), f); |
| 41 | } | 41 | } |
| @@ -143,16 +143,16 @@ public: | |||
| 143 | { | 143 | { |
| 144 | ::sqlite3* dbc = ::sqlite3_db_handle(stmt_.get()); | 144 | ::sqlite3* dbc = ::sqlite3_db_handle(stmt_.get()); |
| 145 | return do_guarded( | 145 | return do_guarded( |
| 146 | dbc, | 146 | dbc, |
| 147 | [&](auto const& guard) -> bool | 147 | [&](auto const& guard) -> bool |
| 148 | { | 148 | { |
| 149 | auto const s = ::sqlite3_step(stmt_.get()); | 149 | auto const s = ::sqlite3_step(stmt_.get()); |
| 150 | if (s == SQLITE_ROW) | 150 | if (s == SQLITE_ROW) |
| 151 | return true; | 151 | return true; |
| 152 | if (s == SQLITE_DONE) | 152 | if (s == SQLITE_DONE) |
| 153 | return false; | 153 | return false; |
| 154 | throw error{guard, s, dbc}; | 154 | throw error{guard, s, dbc}; |
| 155 | }); | 155 | }); |
| 156 | } | 156 | } |
| 157 | 157 | ||
| 158 | auto scan(scannable auto&... args) -> void | 158 | auto scan(scannable auto&... args) -> void |
| @@ -192,15 +192,15 @@ public: | |||
| 192 | auto text(std::string const& param_name, std::string_view str) -> void | 192 | auto text(std::string const& param_name, std::string_view str) -> void |
| 193 | { | 193 | { |
| 194 | int const i = | 194 | int const i = |
| 195 | ::sqlite3_bind_parameter_index(stmt_.get(), param_name.c_str()); | 195 | ::sqlite3_bind_parameter_index(stmt_.get(), param_name.c_str()); |
| 196 | if (i == 0) | 196 | if (i == 0) |
| 197 | throw std::invalid_argument{std::format( | 197 | throw std::invalid_argument{std::format( |
| 198 | "bind: no parameter with name {} found", param_name)}; | 198 | "bind: no parameter with name {} found", param_name)}; |
| 199 | auto str_size = util::int_from_size(str.size()); | 199 | auto str_size = util::int_from_size(str.size()); |
| 200 | if (!str_size.has_value()) | 200 | if (!str_size.has_value()) |
| 201 | throw std::invalid_argument{"bind: provided text is too long"}; | 201 | throw std::invalid_argument{"bind: provided text is too long"}; |
| 202 | if (auto s = ::sqlite3_bind_text( | 202 | if (auto s = ::sqlite3_bind_text( |
| 203 | stmt_.get(), i, str.data(), *str_size, SQLITE_TRANSIENT); | 203 | stmt_.get(), i, str.data(), *str_size, SQLITE_TRANSIENT); |
| 204 | s != SQLITE_OK) | 204 | s != SQLITE_OK) |
| 205 | { | 205 | { |
| 206 | throw error{s}; | 206 | throw error{s}; |
| @@ -232,8 +232,8 @@ public: | |||
| 232 | } | 232 | } |
| 233 | 233 | ||
| 234 | [[nodiscard]] auto query( | 234 | [[nodiscard]] auto query( |
| 235 | std::string const& sql, | 235 | std::string const& sql, |
| 236 | std::function<void(binder&)> const& bf = binder::noop) -> row_reader | 236 | std::function<void(binder&)> const& bf = binder::noop) -> row_reader |
| 237 | { | 237 | { |
| 238 | ::sqlite3_stmt* pstmt = nullptr; | 238 | ::sqlite3_stmt* pstmt = nullptr; |
| 239 | char const* sql_tail = nullptr; | 239 | char const* sql_tail = nullptr; |
| @@ -242,25 +242,25 @@ public: | |||
| 242 | || *sql_size >= std::numeric_limits<int>::max() - 1) | 242 | || *sql_size >= std::numeric_limits<int>::max() - 1) |
| 243 | throw std::invalid_argument{"provided input text too large"}; | 243 | throw std::invalid_argument{"provided input text too large"}; |
| 244 | do_guarded( | 244 | do_guarded( |
| 245 | mut_, | 245 | mut_, |
| 246 | [&](auto const& guard) -> void | 246 | [&](auto const& guard) -> void |
| 247 | { | ||
| 248 | if (auto s = ::sqlite3_prepare_v2( | ||
| 249 | dbc_, sql.data(), *sql_size + 1, &pstmt, &sql_tail); | ||
| 250 | s != SQLITE_OK) | ||
| 251 | { | 247 | { |
| 252 | if (pstmt != nullptr) | 248 | if (auto s = ::sqlite3_prepare_v2( |
| 249 | dbc_, sql.data(), *sql_size + 1, &pstmt, &sql_tail); | ||
| 250 | s != SQLITE_OK) | ||
| 253 | { | 251 | { |
| 254 | // Use contract_assert when having a compiler with | 252 | if (pstmt != nullptr) |
| 255 | // contracts available | 253 | { |
| 256 | ::sqlite3_finalize(pstmt); | 254 | // Use contract_assert when having a compiler with |
| 257 | throw std::logic_error{ | 255 | // contracts available |
| 258 | "expected stmt to be null after failed preparation" | 256 | ::sqlite3_finalize(pstmt); |
| 259 | }; | 257 | throw std::logic_error{ |
| 258 | "expected stmt to be null after failed preparation" | ||
| 259 | }; | ||
| 260 | } | ||
| 261 | throw error{guard, s, dbc_}; | ||
| 260 | } | 262 | } |
| 261 | throw error{guard, s, dbc_}; | 263 | }); |
| 262 | } | ||
| 263 | }); | ||
| 264 | if (!pstmt) | 264 | if (!pstmt) |
| 265 | throw std::invalid_argument{"provided input text contains no SQL"}; | 265 | throw std::invalid_argument{"provided input text contains no SQL"}; |
| 266 | auto stmt = statement{pstmt}; | 266 | auto stmt = statement{pstmt}; |
| @@ -274,8 +274,8 @@ public: | |||
| 274 | } | 274 | } |
| 275 | 275 | ||
| 276 | auto exec( | 276 | auto exec( |
| 277 | std::string const& sql, | 277 | std::string const& sql, |
| 278 | std::function<void(binder&)> const& bf = binder::noop) -> void | 278 | std::function<void(binder&)> const& bf = binder::noop) -> void |
| 279 | { | 279 | { |
| 280 | auto reader = query(sql, bf); | 280 | auto reader = query(sql, bf); |
| 281 | while (reader.next()) | 281 | while (reader.next()) |
| @@ -289,16 +289,16 @@ export auto open(std::string const& filename) -> connection | |||
| 289 | { | 289 | { |
| 290 | ::sqlite3* dbc = nullptr; | 290 | ::sqlite3* dbc = nullptr; |
| 291 | auto s = ::sqlite3_open_v2( | 291 | auto s = ::sqlite3_open_v2( |
| 292 | filename.c_str(), &dbc, | 292 | filename.c_str(), &dbc, |
| 293 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX | 293 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX |
| 294 | | SQLITE_OPEN_EXRESCODE, | 294 | | SQLITE_OPEN_EXRESCODE, |
| 295 | nullptr); | 295 | nullptr); |
| 296 | if (s != SQLITE_OK) | 296 | if (s != SQLITE_OK) |
| 297 | { | 297 | { |
| 298 | if (dbc) | 298 | if (dbc) |
| 299 | { | 299 | { |
| 300 | do_guarded( | 300 | do_guarded( |
| 301 | dbc, [&](auto const& guard) -> void { throw error{guard, s, dbc}; }); | 301 | dbc, [&](auto const& guard) -> void { throw error{guard, s, dbc}; }); |
| 302 | } | 302 | } |
| 303 | else | 303 | else |
| 304 | { | 304 | { |
diff --git a/server/src/srv.cppm b/server/src/srv.cppm index 5763bc5..aef28f4 100644 --- a/server/src/srv.cppm +++ b/server/src/srv.cppm | |||
| @@ -63,17 +63,17 @@ public: | |||
| 63 | auto unwrap() -> gpx::file&& | 63 | auto unwrap() -> gpx::file&& |
| 64 | { | 64 | { |
| 65 | return std::visit( | 65 | return std::visit( |
| 66 | util::overloaded{ | 66 | util::overloaded{ |
| 67 | [](std::exception_ptr ex) -> gpx::file&& | 67 | [](std::exception_ptr ex) -> gpx::file&& |
| 68 | { | 68 | { |
| 69 | if (ex) | 69 | if (ex) |
| 70 | std::rethrow_exception(ex); | 70 | std::rethrow_exception(ex); |
| 71 | else | 71 | else |
| 72 | throw std::runtime_error{"no GPX file parse result available"}; | 72 | throw std::runtime_error{"no GPX file parse result available"}; |
| 73 | }, | ||
| 74 | [](gpx::file&& f) -> gpx::file&& { return std::move(f); }, | ||
| 73 | }, | 75 | }, |
| 74 | [](gpx::file&& f) -> gpx::file&& { return std::move(f); }, | 76 | std::move(res_)); |
| 75 | }, | ||
| 76 | std::move(res_)); | ||
| 77 | } | 77 | } |
| 78 | }; | 78 | }; |
| 79 | 79 | ||
| @@ -100,7 +100,7 @@ struct readable_gpx_body | |||
| 100 | 100 | ||
| 101 | auto | 101 | auto |
| 102 | init(boost::optional<std::uint64_t> /* n */, beast::error_code& ec) noexcept | 102 | init(boost::optional<std::uint64_t> /* n */, beast::error_code& ec) noexcept |
| 103 | -> void | 103 | -> void |
| 104 | { | 104 | { |
| 105 | try | 105 | try |
| 106 | { | 106 | { |
| @@ -114,9 +114,9 @@ struct readable_gpx_body | |||
| 114 | } | 114 | } |
| 115 | } | 115 | } |
| 116 | 116 | ||
| 117 | auto put( | 117 | auto |
| 118 | beast::concepts::const_buffer_sequence auto b, | 118 | put(beast::concepts::const_buffer_sequence auto b, |
| 119 | beast::error_code& ec) noexcept -> std::size_t | 119 | beast::error_code& ec) noexcept -> std::size_t |
| 120 | { | 120 | { |
| 121 | auto total = 0uz; | 121 | auto total = 0uz; |
| 122 | try | 122 | try |
| @@ -125,7 +125,9 @@ struct readable_gpx_body | |||
| 125 | it != net::buffer_sequence_end(b); it++) | 125 | it != net::buffer_sequence_end(b); it++) |
| 126 | { | 126 | { |
| 127 | r_.put( | 127 | r_.put( |
| 128 | std::string_view{static_cast<char const*>(it->data()), it->size()}); | 128 | std::string_view{ |
| 129 | static_cast<char const*>(it->data()), it->size() | ||
| 130 | }); | ||
| 129 | total += it->size(); | 131 | total += it->size(); |
| 130 | } | 132 | } |
| 131 | ec = {}; | 133 | ec = {}; |
| @@ -166,13 +168,13 @@ public: | |||
| 166 | 168 | ||
| 167 | private: | 169 | private: |
| 168 | auto handle_process_gpx(l0_ctx ctx, http::readable_request r) | 170 | auto handle_process_gpx(l0_ctx ctx, http::readable_request r) |
| 169 | -> net::awaitable<http::presponse> | 171 | -> net::awaitable<http::presponse> |
| 170 | { | 172 | { |
| 171 | auto gpx_file = gpx::file{}; | 173 | auto gpx_file = gpx::file{}; |
| 172 | try | 174 | try |
| 173 | { | 175 | { |
| 174 | auto req = | 176 | auto req = |
| 175 | co_await http::read_request<readable_gpx_body>(ctx, std::move(r)); | 177 | co_await http::read_request<readable_gpx_body>(ctx, std::move(r)); |
| 176 | gpx_file = std::move(req->body().unwrap()); | 178 | gpx_file = std::move(req->body().unwrap()); |
| 177 | } | 179 | } |
| 178 | catch (std::exception& ex) | 180 | catch (std::exception& ex) |
| @@ -184,7 +186,7 @@ private: | |||
| 184 | .type_uri = "https://routemon.fautchen.eu/problems/gpx-parse-failed", | 186 | .type_uri = "https://routemon.fautchen.eu/problems/gpx-parse-failed", |
| 185 | }; | 187 | }; |
| 186 | co_return http::problem_rsp( | 188 | co_return http::problem_rsp( |
| 187 | ctx, tpl.instantiate(), http::keep_alive{false}); | 189 | ctx, tpl.instantiate(), http::keep_alive{false}); |
| 188 | } | 190 | } |
| 189 | 191 | ||
| 190 | // TODO: catch handler exceptions and return 500 when raised? | 192 | // TODO: catch handler exceptions and return 500 when raised? |
| @@ -199,11 +201,11 @@ private: | |||
| 199 | "internal-server-error", | 201 | "internal-server-error", |
| 200 | }; | 202 | }; |
| 201 | co_return http::problem_rsp( | 203 | co_return http::problem_rsp( |
| 202 | ctx, tpl.instantiate(), http::keep_alive{true}); | 204 | ctx, tpl.instantiate(), http::keep_alive{true}); |
| 203 | } | 205 | } |
| 204 | 206 | ||
| 205 | auto rsp = http::make_rsp<bhttp::string_body>( | 207 | auto rsp = http::make_rsp<bhttp::string_body>( |
| 206 | bhttp::status::ok, http::keep_alive{true}); | 208 | bhttp::status::ok, http::keep_alive{true}); |
| 207 | rsp.set(bhttp::field::content_type, "application/json"); | 209 | rsp.set(bhttp::field::content_type, "application/json"); |
| 208 | rsp.body() = json::serialize(json::value_from(*mres)); | 210 | rsp.body() = json::serialize(json::value_from(*mres)); |
| 209 | rsp.prepare_payload(); | 211 | rsp.prepare_payload(); |
| @@ -211,14 +213,14 @@ private: | |||
| 211 | } | 213 | } |
| 212 | 214 | ||
| 213 | auto handle_sysinfo(l0_ctx ctx, http::readable_request r) | 215 | auto handle_sysinfo(l0_ctx ctx, http::readable_request r) |
| 214 | -> net::awaitable<http::presponse> | 216 | -> net::awaitable<http::presponse> |
| 215 | { | 217 | { |
| 216 | auto req = | 218 | auto req = |
| 217 | co_await http::read_request<bhttp::empty_body>(ctx, std::move(r)); | 219 | co_await http::read_request<bhttp::empty_body>(ctx, std::move(r)); |
| 218 | auto info = inner_.sysinfo(); | 220 | auto info = inner_.sysinfo(); |
| 219 | 221 | ||
| 220 | auto rsp = http::make_rsp<bhttp::string_body>( | 222 | auto rsp = http::make_rsp<bhttp::string_body>( |
| 221 | bhttp::status::ok, http::keep_alive{true}); | 223 | bhttp::status::ok, http::keep_alive{true}); |
| 222 | rsp.set(bhttp::field::content_type, "application/json"); | 224 | rsp.set(bhttp::field::content_type, "application/json"); |
| 223 | rsp.body() = json::serialize(json::value_from(info)); | 225 | rsp.body() = json::serialize(json::value_from(info)); |
| 224 | rsp.prepare_payload(); | 226 | rsp.prepare_payload(); |
| @@ -255,13 +257,13 @@ export class server | |||
| 255 | http::server<handler::outer_ctx> srv_; | 257 | http::server<handler::outer_ctx> srv_; |
| 256 | 258 | ||
| 257 | static auto make_global_middleware() | 259 | static auto make_global_middleware() |
| 258 | -> http::middleware_t<http::base_ctx, handler::outer_ctx> | 260 | -> http::middleware_t<http::base_ctx, handler::outer_ctx> |
| 259 | { | 261 | { |
| 260 | return http::middleware_compose< | 262 | return http::middleware_compose< |
| 261 | http::base_ctx, http::trace_id_ctx<http::base_ctx>, | 263 | http::base_ctx, http::trace_id_ctx<http::base_ctx>, |
| 262 | http::trace_id_ctx<http::base_ctx>>( | 264 | http::trace_id_ctx<http::base_ctx>>( |
| 263 | http::trace_id_middleware<http::base_ctx>, | 265 | http::trace_id_middleware<http::base_ctx>, |
| 264 | http::lax_cors_middleware<http::trace_id_ctx<http::base_ctx>>); | 266 | http::lax_cors_middleware<http::trace_id_ctx<http::base_ctx>>); |
| 265 | } | 267 | } |
| 266 | 268 | ||
| 267 | public: | 269 | public: |
diff --git a/server/src/time.cppm b/server/src/time.cppm index d77a30b..8f53bd9 100644 --- a/server/src/time.cppm +++ b/server/src/time.cppm | |||
| @@ -31,7 +31,7 @@ public: | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | [[nodiscard]] auto except(period other) const | 33 | [[nodiscard]] auto except(period other) const |
| 34 | -> std::pair<std::optional<period>, std::optional<period>> | 34 | -> std::pair<std::optional<period>, std::optional<period>> |
| 35 | { | 35 | { |
| 36 | auto const before_start = start_; | 36 | auto const before_start = start_; |
| 37 | auto const before_end = other.start(); | 37 | auto const before_end = other.start(); |
diff --git a/server/src/trace.cppm b/server/src/trace.cppm index 3dbed12..35d32f3 100644 --- a/server/src/trace.cppm +++ b/server/src/trace.cppm | |||
| @@ -21,9 +21,9 @@ public: | |||
| 21 | { | 21 | { |
| 22 | namespace chrono = std::chrono; | 22 | namespace chrono = std::chrono; |
| 23 | auto const unix_time_ms_signed = static_cast<std::int64_t>( | 23 | auto const unix_time_ms_signed = static_cast<std::int64_t>( |
| 24 | chrono::duration_cast<chrono::milliseconds>( | 24 | chrono::duration_cast<chrono::milliseconds>( |
| 25 | chrono::system_clock::now().time_since_epoch()) | 25 | chrono::system_clock::now().time_since_epoch()) |
| 26 | .count()); | 26 | .count()); |
| 27 | if (unix_time_ms_signed < 0) | 27 | if (unix_time_ms_signed < 0) |
| 28 | throw std::runtime_error{"system time before UNIX epoch"}; | 28 | throw std::runtime_error{"system time before UNIX epoch"}; |
| 29 | auto const unix_time_ms = static_cast<std::uint64_t>(unix_time_ms_signed); | 29 | auto const unix_time_ms = static_cast<std::uint64_t>(unix_time_ms_signed); |
| @@ -36,8 +36,8 @@ public: | |||
| 36 | { | 36 | { |
| 37 | unsigned long e = ERR_get_error(); | 37 | unsigned long e = ERR_get_error(); |
| 38 | throw std::runtime_error{std::format( | 38 | throw std::runtime_error{std::format( |
| 39 | "failed to generate UUID(v7): {} ({}, code {})", | 39 | "failed to generate UUID(v7): {} ({}, code {})", |
| 40 | ERR_reason_error_string(e), ERR_lib_error_string(e), e)}; | 40 | ERR_reason_error_string(e), ERR_lib_error_string(e), e)}; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | auto version = std::uint64_t{0b0111}; | 43 | auto version = std::uint64_t{0b0111}; |
| @@ -67,8 +67,8 @@ public: | |||
| 67 | auto low_low = (low_ & 0x0000'ffff'ffff'ffff) >> 0; | 67 | auto low_low = (low_ & 0x0000'ffff'ffff'ffff) >> 0; |
| 68 | 68 | ||
| 69 | std::format_to( | 69 | std::format_to( |
| 70 | target.begin(), "{:0>8x}-{:0>4x}-{:0>4x}-{:0>4x}-{:0>12x}", high_high, | 70 | target.begin(), "{:0>8x}-{:0>4x}-{:0>4x}-{:0>4x}-{:0>12x}", high_high, |
| 71 | high_low_high, low_low_high, high_low, low_low); | 71 | high_low_high, low_low_high, high_low, low_low); |
| 72 | target.back() = '\0'; | 72 | target.back() = '\0'; |
| 73 | } | 73 | } |
| 74 | }; | 74 | }; |
diff --git a/server/src/util.cppm b/server/src/util.cppm index 2e20e8b..bdccf10 100644 --- a/server/src/util.cppm +++ b/server/src/util.cppm | |||
| @@ -14,9 +14,9 @@ struct overloaded : Ts... | |||
| 14 | }; | 14 | }; |
| 15 | 15 | ||
| 16 | export constexpr auto parse_double( | 16 | export constexpr auto parse_double( |
| 17 | std::string_view s, | 17 | std::string_view s, |
| 18 | std::chars_format fmt = std::chars_format::general) noexcept | 18 | std::chars_format fmt = std::chars_format::general) noexcept |
| 19 | -> std::optional<double> | 19 | -> std::optional<double> |
| 20 | { | 20 | { |
| 21 | auto x = 0.0; | 21 | auto x = 0.0; |
| 22 | auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); | 22 | auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); |
| @@ -31,9 +31,9 @@ export constexpr auto parse_double( | |||
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | export constexpr auto parse_float( | 33 | export constexpr auto parse_float( |
| 34 | std::string_view s, | 34 | std::string_view s, |
| 35 | std::chars_format fmt = std::chars_format::general) noexcept | 35 | std::chars_format fmt = std::chars_format::general) noexcept |
| 36 | -> std::optional<float> | 36 | -> std::optional<float> |
| 37 | { | 37 | { |
| 38 | auto x = 0.0; | 38 | auto x = 0.0; |
| 39 | auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); | 39 | auto [_, ec] = std::from_chars(s.data(), s.data() + s.size(), x, fmt); |
| @@ -62,7 +62,7 @@ public: | |||
| 62 | static auto nil() -> std::shared_ptr<aolist<T>> { return nullptr; } | 62 | static auto nil() -> std::shared_ptr<aolist<T>> { return nullptr; } |
| 63 | 63 | ||
| 64 | static auto cons(T v, std::shared_ptr<aolist<T> const> l) | 64 | static auto cons(T v, std::shared_ptr<aolist<T> const> l) |
| 65 | -> std::shared_ptr<aolist<T>> | 65 | -> std::shared_ptr<aolist<T>> |
| 66 | { | 66 | { |
| 67 | return std::shared_ptr<aolist<T>>{new aolist<T>{v, l}}; | 67 | return std::shared_ptr<aolist<T>>{new aolist<T>{v, l}}; |
| 68 | } | 68 | } |
| @@ -75,8 +75,8 @@ public: | |||
| 75 | export constexpr auto size_from_int(int x) -> std::optional<std::size_t> | 75 | export constexpr auto size_from_int(int x) -> std::optional<std::size_t> |
| 76 | { | 76 | { |
| 77 | static_assert( | 77 | static_assert( |
| 78 | sizeof(int) <= sizeof(std::size_t), | 78 | sizeof(int) <= sizeof(std::size_t), |
| 79 | "cannot cast int to smaller size_t type"); | 79 | "cannot cast int to smaller size_t type"); |
| 80 | if (x < 0) | 80 | if (x < 0) |
| 81 | return std::nullopt; | 81 | return std::nullopt; |
| 82 | return static_cast<std::size_t>(x); | 82 | return static_cast<std::size_t>(x); |
| @@ -155,7 +155,8 @@ public: | |||
| 155 | { | 155 | { |
| 156 | s_ = rhs.s_; | 156 | s_ = rhs.s_; |
| 157 | length_.store( | 157 | length_.store( |
| 158 | rhs.length_.load(std::memory_order_acquire), std::memory_order_release); | 158 | rhs.length_.load(std::memory_order_acquire), |
| 159 | std::memory_order_release); | ||
| 159 | } | 160 | } |
| 160 | return *this; | 161 | return *this; |
| 161 | } | 162 | } |
| @@ -202,7 +203,7 @@ public: | |||
| 202 | }; | 203 | }; |
| 203 | 204 | ||
| 204 | constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept | 205 | constexpr auto operator""_zsv(char const* s, std::size_t length) noexcept |
| 205 | -> zstring_view | 206 | -> zstring_view |
| 206 | { | 207 | { |
| 207 | return zstring_view{s, length}; | 208 | return zstring_view{s, length}; |
| 208 | } | 209 | } |
| @@ -213,7 +214,7 @@ auto operator==(zstring_view lhs, zstring_view rhs) -> bool | |||
| 213 | } | 214 | } |
| 214 | 215 | ||
| 215 | export constexpr auto split_on(std::string_view s, char c) | 216 | export constexpr auto split_on(std::string_view s, char c) |
| 216 | -> std::pair<std::string_view, std::optional<std::string_view>> | 217 | -> std::pair<std::string_view, std::optional<std::string_view>> |
| 217 | { | 218 | { |
| 218 | if (auto i = s.find(c); i != std::string_view::npos) | 219 | if (auto i = s.find(c); i != std::string_view::npos) |
| 219 | return std::make_pair(s.substr(0, i), s.substr(i + 1)); | 220 | return std::make_pair(s.substr(0, i), s.substr(i + 1)); |
diff --git a/server/src/xml.cpp b/server/src/xml.cpp index 7cf4415..c1b4358 100644 --- a/server/src/xml.cpp +++ b/server/src/xml.cpp | |||
| @@ -22,7 +22,7 @@ executor::executor() : p_{XML_ParserCreateNS("UTF-8", detail::qname_sep)} | |||
| 22 | XML_SetProcessingInstructionHandler(p_, handle_processing_instructions); | 22 | XML_SetProcessingInstructionHandler(p_, handle_processing_instructions); |
| 23 | XML_SetExternalEntityRefHandler(p_, handle_external_entity_ref); | 23 | XML_SetExternalEntityRefHandler(p_, handle_external_entity_ref); |
| 24 | XML_SetNamespaceDeclHandler( | 24 | XML_SetNamespaceDeclHandler( |
| 25 | p_, handle_start_namespace_decl, handle_end_namespace_decl); | 25 | p_, handle_start_namespace_decl, handle_end_namespace_decl); |
| 26 | XML_SetXmlDeclHandler(p_, handle_xml_decl); | 26 | XML_SetXmlDeclHandler(p_, handle_xml_decl); |
| 27 | } | 27 | } |
| 28 | 28 | ||
| @@ -41,7 +41,7 @@ auto executor::read(std::string_view xml, bool is_final) -> void | |||
| 41 | throw std::runtime_error{"refusing to restart parser that was thrown in"}; | 41 | throw std::runtime_error{"refusing to restart parser that was thrown in"}; |
| 42 | // TODO: narrow_cast | 42 | // TODO: narrow_cast |
| 43 | if (auto s = | 43 | if (auto s = |
| 44 | XML_Parse(p_, xml.data(), static_cast<int>(xml.size()), is_final); | 44 | XML_Parse(p_, xml.data(), static_cast<int>(xml.size()), is_final); |
| 45 | s != XML_STATUS_OK) | 45 | s != XML_STATUS_OK) |
| 46 | { | 46 | { |
| 47 | auto errc = XML_GetErrorCode(p_); | 47 | auto errc = XML_GetErrorCode(p_); |
| @@ -53,7 +53,7 @@ auto executor::read(std::string_view xml, bool is_final) -> void | |||
| 53 | else | 53 | else |
| 54 | { | 54 | { |
| 55 | throw std::runtime_error{std::format( | 55 | throw std::runtime_error{std::format( |
| 56 | "failed to parse XML: {}", XML_ErrorString(errc))}; | 56 | "failed to parse XML: {}", XML_ErrorString(errc))}; |
| 57 | } | 57 | } |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
diff --git a/server/src/xml.cppm b/server/src/xml.cppm index 0c319e6..4648047 100644 --- a/server/src/xml.cppm +++ b/server/src/xml.cppm | |||
| @@ -66,7 +66,7 @@ public: | |||
| 66 | }; | 66 | }; |
| 67 | 67 | ||
| 68 | inline explicit attribute_view_iterator( | 68 | inline explicit attribute_view_iterator( |
| 69 | std::string_view default_ns_uri, char const* const* attrs) | 69 | std::string_view default_ns_uri, char const* const* attrs) |
| 70 | : default_ns_uri_{default_ns_uri}, attrs_{attrs} | 70 | : default_ns_uri_{default_ns_uri}, attrs_{attrs} |
| 71 | { | 71 | { |
| 72 | } | 72 | } |
| @@ -105,7 +105,7 @@ class attribute_view : std::ranges::view_base | |||
| 105 | 105 | ||
| 106 | public: | 106 | public: |
| 107 | inline explicit attribute_view( | 107 | inline explicit attribute_view( |
| 108 | std::string_view default_ns_uri, char const** attrs) | 108 | std::string_view default_ns_uri, char const** attrs) |
| 109 | : default_ns_uri_{default_ns_uri}, | 109 | : default_ns_uri_{default_ns_uri}, |
| 110 | attrs_{const_cast<char const* const*>(attrs)} | 110 | attrs_{const_cast<char const* const*>(attrs)} |
| 111 | { | 111 | { |
| @@ -122,7 +122,7 @@ public: | |||
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | inline auto lookup(qname_view want) | 124 | inline auto lookup(qname_view want) |
| 125 | -> std::optional<util::not_null<util::lazy_zstring_view>> | 125 | -> std::optional<util::not_null<util::lazy_zstring_view>> |
| 126 | { | 126 | { |
| 127 | for (auto const& [name, v] : *this) | 127 | for (auto const& [name, v] : *this) |
| 128 | { | 128 | { |
| @@ -200,8 +200,8 @@ struct eof_event | |||
| 200 | }; | 200 | }; |
| 201 | 201 | ||
| 202 | using event = std::variant< | 202 | using event = std::variant< |
| 203 | start_element_event, end_element_event, character_data_event, | 203 | start_element_event, end_element_event, character_data_event, |
| 204 | processing_instructions_event, xml_decl_event, eof_event>; | 204 | processing_instructions_event, xml_decl_event, eof_event>; |
| 205 | 205 | ||
| 206 | template <class T> | 206 | template <class T> |
| 207 | concept event_type = requires(event ev) { std::get<T>(ev); }; | 207 | concept event_type = requires(event ev) { std::get<T>(ev); }; |
| @@ -237,7 +237,7 @@ public: | |||
| 237 | } | 237 | } |
| 238 | 238 | ||
| 239 | [[nodiscard]] inline auto continuation() const | 239 | [[nodiscard]] inline auto continuation() const |
| 240 | -> std::coroutine_handle<promise_base> | 240 | -> std::coroutine_handle<promise_base> |
| 241 | { | 241 | { |
| 242 | return continuation_; | 242 | return continuation_; |
| 243 | } | 243 | } |
| @@ -285,11 +285,11 @@ class executor | |||
| 285 | s != XML_STATUS_OK) | 285 | s != XML_STATUS_OK) |
| 286 | { | 286 | { |
| 287 | ex_ = std::make_exception_ptr( | 287 | ex_ = std::make_exception_ptr( |
| 288 | std::runtime_error{"unexpected error when stopping XML parser"}); | 288 | std::runtime_error{"unexpected error when stopping XML parser"}); |
| 289 | return; | 289 | return; |
| 290 | } | 290 | } |
| 291 | ex_ = std::make_exception_ptr( | 291 | ex_ = std::make_exception_ptr( |
| 292 | std::runtime_error{"parser did not consume entire XML document"}); | 292 | std::runtime_error{"parser did not consume entire XML document"}); |
| 293 | return; | 293 | return; |
| 294 | } | 294 | } |
| 295 | continuation_.resume(); | 295 | continuation_.resume(); |
| @@ -302,74 +302,74 @@ class executor | |||
| 302 | 302 | ||
| 303 | static auto | 303 | static auto |
| 304 | handle_start_element(void* ctx, char const* name, char const** attrs) noexcept | 304 | handle_start_element(void* ctx, char const* name, char const** attrs) noexcept |
| 305 | -> void | 305 | -> void |
| 306 | { | 306 | { |
| 307 | auto qname = detail::split_name(name); | 307 | auto qname = detail::split_name(name); |
| 308 | static_cast<executor*>(ctx)->try_handle_event( | 308 | static_cast<executor*>(ctx)->try_handle_event( |
| 309 | start_element_event{ | 309 | start_element_event{ |
| 310 | .name = qname, | 310 | .name = qname, |
| 311 | .attrs = attribute_view{qname.ns_uri, attrs}, | 311 | .attrs = attribute_view{qname.ns_uri, attrs}, |
| 312 | }); | 312 | }); |
| 313 | } | 313 | } |
| 314 | 314 | ||
| 315 | static auto handle_end_element(void* ctx, char const* name) noexcept -> void | 315 | static auto handle_end_element(void* ctx, char const* name) noexcept -> void |
| 316 | { | 316 | { |
| 317 | static_cast<executor*>(ctx)->try_handle_event( | 317 | static_cast<executor*>(ctx)->try_handle_event( |
| 318 | end_element_event{ | 318 | end_element_event{ |
| 319 | .name = detail::split_name(name), | 319 | .name = detail::split_name(name), |
| 320 | }); | 320 | }); |
| 321 | } | 321 | } |
| 322 | 322 | ||
| 323 | static auto handle_character_data(void* ctx, char const* s, int len) noexcept | 323 | static auto handle_character_data(void* ctx, char const* s, int len) noexcept |
| 324 | -> void | 324 | -> void |
| 325 | { | 325 | { |
| 326 | static_cast<executor*>(ctx)->try_handle_event( | 326 | static_cast<executor*>(ctx)->try_handle_event( |
| 327 | character_data_event{ | 327 | character_data_event{ |
| 328 | .data = std::string_view{s, static_cast<std::size_t>(len)}, | 328 | .data = std::string_view{s, static_cast<std::size_t>(len)}, |
| 329 | }); | 329 | }); |
| 330 | } | 330 | } |
| 331 | 331 | ||
| 332 | static auto handle_processing_instructions( | 332 | static auto handle_processing_instructions( |
| 333 | void* ctx, char const* target, char const* data) noexcept -> void | 333 | void* ctx, char const* target, char const* data) noexcept -> void |
| 334 | { | 334 | { |
| 335 | static_cast<executor*>(ctx)->try_handle_event( | 335 | static_cast<executor*>(ctx)->try_handle_event( |
| 336 | processing_instructions_event{ | 336 | processing_instructions_event{ |
| 337 | .target = util::lazy_zstring_view{target}, | 337 | .target = util::lazy_zstring_view{target}, |
| 338 | .data = util::lazy_zstring_view{data}, | 338 | .data = util::lazy_zstring_view{data}, |
| 339 | }); | 339 | }); |
| 340 | } | 340 | } |
| 341 | 341 | ||
| 342 | static auto handle_external_entity_ref( | 342 | static auto handle_external_entity_ref( |
| 343 | XML_Parser, char const* /* context */, char const* /* base */, | 343 | XML_Parser, char const* /* context */, char const* /* base */, |
| 344 | char const* /* system_id */, char const* /* public_id */) noexcept -> int | 344 | char const* /* system_id */, char const* /* public_id */) noexcept -> int |
| 345 | { | 345 | { |
| 346 | return XML_STATUS_ERROR; | 346 | return XML_STATUS_ERROR; |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | static auto handle_start_namespace_decl( | 349 | static auto handle_start_namespace_decl( |
| 350 | void* ctx, char const* prefix, char const* uri) noexcept -> void | 350 | void* ctx, char const* prefix, char const* uri) noexcept -> void |
| 351 | { | 351 | { |
| 352 | if (prefix) | 352 | if (prefix) |
| 353 | { | 353 | { |
| 354 | static_cast<executor*>(ctx) | 354 | static_cast<executor*>(ctx) |
| 355 | ->namespaces_[std::string_view{prefix}] | 355 | ->namespaces_[std::string_view{prefix}] |
| 356 | .emplace_back(uri); | 356 | .emplace_back(uri); |
| 357 | } | 357 | } |
| 358 | else | 358 | else |
| 359 | { | 359 | { |
| 360 | static_cast<executor*>(ctx)->default_namespace_.push_back( | 360 | static_cast<executor*>(ctx)->default_namespace_.push_back( |
| 361 | uri ? std::make_optional<std::string>(uri) : std::nullopt); | 361 | uri ? std::make_optional<std::string>(uri) : std::nullopt); |
| 362 | } | 362 | } |
| 363 | } | 363 | } |
| 364 | 364 | ||
| 365 | static auto handle_end_namespace_decl(void* ctx, char const* prefix) noexcept | 365 | static auto handle_end_namespace_decl(void* ctx, char const* prefix) noexcept |
| 366 | -> void | 366 | -> void |
| 367 | { | 367 | { |
| 368 | if (prefix) | 368 | if (prefix) |
| 369 | { | 369 | { |
| 370 | static_cast<executor*>(ctx) | 370 | static_cast<executor*>(ctx) |
| 371 | ->namespaces_[std::string_view{prefix}] | 371 | ->namespaces_[std::string_view{prefix}] |
| 372 | .pop_back(); | 372 | .pop_back(); |
| 373 | } | 373 | } |
| 374 | else | 374 | else |
| 375 | { | 375 | { |
| @@ -378,16 +378,16 @@ class executor | |||
| 378 | } | 378 | } |
| 379 | 379 | ||
| 380 | static auto handle_xml_decl( | 380 | static auto handle_xml_decl( |
| 381 | void* ctx, char const* version, char const* encoding, | 381 | void* ctx, char const* version, char const* encoding, |
| 382 | int standalone) noexcept -> void | 382 | int standalone) noexcept -> void |
| 383 | { | 383 | { |
| 384 | static_cast<executor*>(ctx)->try_handle_event( | 384 | static_cast<executor*>(ctx)->try_handle_event( |
| 385 | xml_decl_event{ | 385 | xml_decl_event{ |
| 386 | .version = util::lazy_zstring_view{version}, | 386 | .version = util::lazy_zstring_view{version}, |
| 387 | .encoding = util::lazy_zstring_view{encoding}, | 387 | .encoding = util::lazy_zstring_view{encoding}, |
| 388 | .standalone = | 388 | .standalone = standalone < 0 ? std::nullopt |
| 389 | standalone < 0 ? std::nullopt : std::make_optional(standalone > 0), | 389 | : std::make_optional(standalone > 0), |
| 390 | }); | 390 | }); |
| 391 | } | 391 | } |
| 392 | 392 | ||
| 393 | inline auto advance_flag() -> bool { return advance_; } | 393 | inline auto advance_flag() -> bool { return advance_; } |
| @@ -431,7 +431,7 @@ public: | |||
| 431 | inline auto event() const -> std::optional<event> const& { return ev_; } | 431 | inline auto event() const -> std::optional<event> const& { return ev_; } |
| 432 | 432 | ||
| 433 | inline auto resolve_namespace(std::string_view prefix) | 433 | inline auto resolve_namespace(std::string_view prefix) |
| 434 | -> std::optional<std::string_view> | 434 | -> std::optional<std::string_view> |
| 435 | { | 435 | { |
| 436 | if (auto it = namespaces_.find(prefix); | 436 | if (auto it = namespaces_.find(prefix); |
| 437 | it != namespaces_.end() && !it->second.empty()) | 437 | it != namespaces_.end() && !it->second.empty()) |
| @@ -573,7 +573,7 @@ public: | |||
| 573 | return false; | 573 | return false; |
| 574 | } | 574 | } |
| 575 | auto await_suspend(std::coroutine_handle<promise<T>> h) | 575 | auto await_suspend(std::coroutine_handle<promise<T>> h) |
| 576 | -> std::coroutine_handle<> | 576 | -> std::coroutine_handle<> |
| 577 | { | 577 | { |
| 578 | // Passed coroutine handle will be the same as | 578 | // Passed coroutine handle will be the same as |
| 579 | // parser<T>::handle_type::from_promise(*this) | 579 | // parser<T>::handle_type::from_promise(*this) |
| @@ -621,13 +621,13 @@ using parser_result_t = T::result_type; | |||
| 621 | 621 | ||
| 622 | template <class T, class... Args> | 622 | template <class T, class... Args> |
| 623 | concept parser_invocable = | 623 | concept parser_invocable = |
| 624 | std::invocable<T, Args...> | 624 | std::invocable<T, Args...> |
| 625 | && parser_of<std::invoke_result_t<T, Args...>, unconstrained>; | 625 | && parser_of<std::invoke_result_t<T, Args...>, unconstrained>; |
| 626 | 626 | ||
| 627 | template <class Fn, class... Args> | 627 | template <class Fn, class... Args> |
| 628 | requires parser_invocable<Fn, Args...> | 628 | requires parser_invocable<Fn, Args...> |
| 629 | using parser_invoke_result_t = | 629 | using parser_invoke_result_t = |
| 630 | parser_result_t<std::invoke_result_t<Fn, Args...>>; | 630 | parser_result_t<std::invoke_result_t<Fn, Args...>>; |
| 631 | 631 | ||
| 632 | template <event_type T> | 632 | template <event_type T> |
| 633 | auto expect_event(executor_ref e) -> parser<T> | 633 | auto expect_event(executor_ref e) -> parser<T> |
| @@ -637,28 +637,28 @@ auto expect_event(executor_ref e) -> parser<T> | |||
| 637 | { | 637 | { |
| 638 | auto pos = e->position(); | 638 | auto pos = e->position(); |
| 639 | throw std::runtime_error{std::format( | 639 | throw std::runtime_error{std::format( |
| 640 | "at {}:{}: unexpected event type, have {}", pos.line, pos.col, | 640 | "at {}:{}: unexpected event type, have {}", pos.line, pos.col, |
| 641 | ev.index())}; | 641 | ev.index())}; |
| 642 | } | 642 | } |
| 643 | e->set_advance_flag(); | 643 | e->set_advance_flag(); |
| 644 | co_return std::get<T>(ev); | 644 | co_return std::get<T>(ev); |
| 645 | } | 645 | } |
| 646 | 646 | ||
| 647 | inline auto expect_start_element(executor_ref e, qname_view want) | 647 | inline auto expect_start_element(executor_ref e, qname_view want) |
| 648 | -> parser<attribute_view> | 648 | -> parser<attribute_view> |
| 649 | { | 649 | { |
| 650 | auto ev = co_await expect_event<start_element_event>(e); | 650 | auto ev = co_await expect_event<start_element_event>(e); |
| 651 | if (ev.name != want) | 651 | if (ev.name != want) |
| 652 | { | 652 | { |
| 653 | auto pos = e->position(); | 653 | auto pos = e->position(); |
| 654 | throw std::runtime_error{std::format( | 654 | throw std::runtime_error{std::format( |
| 655 | "at {}:{}: unexpected element started", pos.line, pos.col)}; | 655 | "at {}:{}: unexpected element started", pos.line, pos.col)}; |
| 656 | } | 656 | } |
| 657 | co_return ev.attrs; | 657 | co_return ev.attrs; |
| 658 | } | 658 | } |
| 659 | 659 | ||
| 660 | inline auto allow_start_element(executor_ref e, qname_view want) | 660 | inline auto allow_start_element(executor_ref e, qname_view want) |
| 661 | -> parser<std::optional<attribute_view>> | 661 | -> parser<std::optional<attribute_view>> |
| 662 | { | 662 | { |
| 663 | auto ev = co_await current_event(e); | 663 | auto ev = co_await current_event(e); |
| 664 | if (auto const* pev = std::get_if<start_element_event>(&ev)) | 664 | if (auto const* pev = std::get_if<start_element_event>(&ev)) |
| @@ -707,9 +707,9 @@ inline auto ignore_whitespace(executor_ref e) -> parser<void> | |||
| 707 | } | 707 | } |
| 708 | 708 | ||
| 709 | auto expect_element( | 709 | auto expect_element( |
| 710 | executor_ref e, qname_view want, | 710 | executor_ref e, qname_view want, |
| 711 | parser_invocable<executor_ref, attribute_view> auto p) | 711 | parser_invocable<executor_ref, attribute_view> auto p) |
| 712 | -> parser<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> | 712 | -> parser<parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> |
| 713 | { | 713 | { |
| 714 | co_await ignore_whitespace(e); | 714 | co_await ignore_whitespace(e); |
| 715 | auto attrs = co_await expect_start_element(e, want); | 715 | auto attrs = co_await expect_start_element(e, want); |
| @@ -717,14 +717,14 @@ auto expect_element( | |||
| 717 | co_await expect_end_element(e, want); | 717 | co_await expect_end_element(e, want); |
| 718 | co_await ignore_whitespace(e); | 718 | co_await ignore_whitespace(e); |
| 719 | co_return std::forward< | 719 | co_return std::forward< |
| 720 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(res); | 720 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>(res); |
| 721 | } | 721 | } |
| 722 | 722 | ||
| 723 | auto allow_element( | 723 | auto allow_element( |
| 724 | executor_ref e, qname_view want, | 724 | executor_ref e, qname_view want, |
| 725 | parser_invocable<executor_ref, attribute_view> auto p) | 725 | parser_invocable<executor_ref, attribute_view> auto p) |
| 726 | -> parser<std::optional< | 726 | -> parser<std::optional< |
| 727 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>> | 727 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>> |
| 728 | { | 728 | { |
| 729 | co_await ignore_whitespace(e); | 729 | co_await ignore_whitespace(e); |
| 730 | if (auto mattrs = co_await allow_start_element(e, want)) | 730 | if (auto mattrs = co_await allow_start_element(e, want)) |
| @@ -733,18 +733,18 @@ auto allow_element( | |||
| 733 | co_await expect_end_element(e, want); | 733 | co_await expect_end_element(e, want); |
| 734 | co_await ignore_whitespace(e); | 734 | co_await ignore_whitespace(e); |
| 735 | co_return std::make_optional( | 735 | co_return std::make_optional( |
| 736 | std::forward< | 736 | std::forward< |
| 737 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>( | 737 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>>( |
| 738 | res)); | 738 | res)); |
| 739 | } | 739 | } |
| 740 | co_return std::nullopt; | 740 | co_return std::nullopt; |
| 741 | } | 741 | } |
| 742 | 742 | ||
| 743 | auto allow_element( | 743 | auto allow_element( |
| 744 | executor_ref e, qname_view want, | 744 | executor_ref e, qname_view want, |
| 745 | parser_invocable<executor_ref, attribute_view> auto p) -> parser<bool> | 745 | parser_invocable<executor_ref, attribute_view> auto p) -> parser<bool> |
| 746 | requires std::is_void_v< | 746 | requires std::is_void_v< |
| 747 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> | 747 | parser_invoke_result_t<decltype(p), executor_ref, attribute_view>> |
| 748 | { | 748 | { |
| 749 | co_await ignore_whitespace(e); | 749 | co_await ignore_whitespace(e); |
| 750 | if (auto mattrs = co_await allow_start_element(e, want)) | 750 | if (auto mattrs = co_await allow_start_element(e, want)) |
| @@ -759,7 +759,7 @@ auto allow_element( | |||
| 759 | 759 | ||
| 760 | inline auto | 760 | inline auto |
| 761 | ignore_contents(executor_ref e, std::optional<qname_view> muntil = std::nullopt) | 761 | ignore_contents(executor_ref e, std::optional<qname_view> muntil = std::nullopt) |
| 762 | -> parser<void> | 762 | -> parser<void> |
| 763 | { | 763 | { |
| 764 | std::size_t depth = 0; | 764 | std::size_t depth = 0; |
| 765 | while (true) | 765 | while (true) |
| @@ -791,7 +791,7 @@ ignore_contents(executor_ref e, std::optional<qname_view> muntil = std::nullopt) | |||
| 791 | } | 791 | } |
| 792 | } | 792 | } |
| 793 | inline auto ignore_element_contents(executor_ref e, attribute_view) | 793 | inline auto ignore_element_contents(executor_ref e, attribute_view) |
| 794 | -> parser<void> | 794 | -> parser<void> |
| 795 | { | 795 | { |
| 796 | co_await ignore_contents(e); | 796 | co_await ignore_contents(e); |
| 797 | } | 797 | } |
| @@ -814,7 +814,7 @@ inline auto read_string(executor_ref e) -> parser<std::string> | |||
| 814 | } | 814 | } |
| 815 | } | 815 | } |
| 816 | inline auto read_string_contents(executor_ref e, attribute_view) | 816 | inline auto read_string_contents(executor_ref e, attribute_view) |
| 817 | -> parser<std::string> | 817 | -> parser<std::string> |
| 818 | { | 818 | { |
| 819 | co_return co_await read_string(e); | 819 | co_return co_await read_string(e); |
| 820 | } | 820 | } |
| @@ -823,7 +823,7 @@ template <auto f> | |||
| 823 | auto hohalo() | 823 | auto hohalo() |
| 824 | { | 824 | { |
| 825 | return []<class... Args>( | 825 | return []<class... Args>( |
| 826 | Args&&... args) -> std::invoke_result_t<decltype(f), Args...> | 826 | Args&&... args) -> std::invoke_result_t<decltype(f), Args...> |
| 827 | { co_return co_await f(std::forward<Args>(args)...); }; | 827 | { co_return co_await f(std::forward<Args>(args)...); }; |
| 828 | } | 828 | } |
| 829 | 829 | ||