summaryrefslogtreecommitdiffstats
path: root/server/src/xml.cpp
blob: c1b4358a99e19ac8504b032dceded862db4fd049 (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
62
63
64
65
66
67
68
69
70
71
72
73
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