summaryrefslogtreecommitdiffstats
path: root/server/src/xml.cpp
blob: cad42d13a3af1149758055f1aa9b5379f3ac8c24 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
module;

#include <cassert>
#include <expat.h>

module routemon:xml$impl;

import :xml;

namespace routemon::xml {

  auto qname_view::operator==(qname_view const& rhs) const -> bool {
    return ns_uri == rhs.ns_uri && local == rhs.local;
  }

  executor::executor() :
    p_{XML_ParserCreateNS("UTF-8", detail::qname_sep)}
  {
    XML_SetUserData(p_, this);
    XML_SetElementHandler(p_, handle_start_element, handle_end_element);
    XML_SetCharacterDataHandler(p_, handle_character_data);
    XML_SetProcessingInstructionHandler(p_, handle_processing_instructions);
    XML_SetExternalEntityRefHandler(p_, handle_external_entity_ref);
    XML_SetNamespaceDeclHandler(p_, handle_start_namespace_decl, handle_end_namespace_decl);
    XML_SetXmlDeclHandler(p_, handle_xml_decl);
  }

  executor::~executor() {
    XML_ParserFree(p_);
  }

  auto executor::start() -> void {
    continuation_.resume();
    if (ex_) std::rethrow_exception(ex_);
  }

  auto executor::read(std::string_view xml, bool is_final) -> void {
    if (ex_)
      throw std::runtime_error{"refusing to restart parser that was thrown in"};
    // TODO: narrow_cast
    if (auto s = XML_Parse(p_, xml.data(), static_cast<int>(xml.size()), is_final); s != XML_STATUS_OK) {
      auto errc = XML_GetErrorCode(p_);
      if (errc == XML_ERROR_ABORTED) {
        assert(ex_);
        std::rethrow_exception(ex_);
      } else {
        throw std::runtime_error{std::format("failed to parse XML: {}", XML_ErrorString(errc))};
      }
    }
  }

  auto executor::end() -> void {
    if (ex_)
      throw std::runtime_error{"refusing to restart parser that was thrown in"};
    ev_ = eof_event{};
    advance_ = false;
    while (continuation_) continuation_.resume();
    if (ex_) std::rethrow_exception(ex_);
  }

} // namespace routemon::xml