summaryrefslogtreecommitdiffstats
path: root/server/src/main.cpp
blob: a40b0b052c6c285922ee691c67a816334eed022e (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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#include <malloc.h>  // for malloc_trim(3)
#include <boost/asio.hpp>

import std;
import routemon;

namespace chrono = std::chrono;
namespace net = boost::asio;

enum class exit_status {
  failure,
  bad_usage,
};

auto real_main(std::span<char const*> args) -> exit_status {
  auto sink = routemon::log::make_sink(routemon::log::level::info);
  auto l = routemon::log::logger{sink};

  if (args.size() != 2) {
    l.error("Fatal: expected exactly one argument (the configuration file location), got {}", args.size() - 1);
    return exit_status::bad_usage;
  }
  auto const* config_filename = args[1];

  auto lgen = routemon::locale::make_generator();
  auto default_locale = lgen->generate("en_US.UTF-8");
  auto locales = {
    default_locale,
    lgen->generate("nl_NL.UTF-8"),
    lgen->generate("de_DE.UTF-8"),
    lgen->generate("en_GB.UTF-8"),
  };
  auto lsel = routemon::locale::selector{locales, default_locale, lgen};

  auto ioc = net::io_context{1 /* concurrency hint */};

  auto config = routemon::config::app{};
  try {
    config = routemon::config::load_file(config_filename);
  } catch (std::exception const& e) {
    l.with("filename", std::string_view{config_filename}).
      error("Failed to load configuration: {}", e.what());
    return exit_status::failure;
  }
  sink->set_level(config.logger.level);
  // auto rwgps_client = routemon::rwgps::client{ioc, l, config.rwgps.api_key, config.rwgps.auth_token};
  // for (auto route : rwgps_client.get_all_routes()) {
  //   l.info("Route {} (user {}): {} @ {}", route.id, route.user_id, route.name, route.url);
  // }

  auto dbc = std::shared_ptr<routemon::database::connection>{};
  try {
    dbc = routemon::database::open(config.database.sqlite3_filename);
  } catch (std::exception const& e) {
    l.with("filename", config.database.sqlite3_filename).
      error("Failed to open database: {}", e.what());
    return exit_status::failure;
  }

  l.with("filename", config.situations.datex2_filename).
    info("Loading situations");
  auto const before_load = chrono::steady_clock::now();
  auto d2loader = routemon::datex2::loader{};
  auto pub = routemon::datex2::situation_publication{};
  try {
    pub = d2loader.load_situation_publication(config.situations.datex2_filename);
  } catch (std::exception const& e) {
    l.error("Failed to load DATEX II situations publication: {}", e.what());
    return exit_status::failure;
  }
  if (!d2loader.warnings().empty()) {
    auto const& warns = d2loader.warnings();
    l.warn("Encountered {} unique warnings while loading DATEX II situations publication", warns.size());
    auto i = 0uz;
    for (auto it = warns.begin(); it != warns.end(); it = warns.upper_bound(*it)) {
      l.warn("Warning {} (appeared {}×): {}", ++i, warns.count(*it), *it);
    }
  }
  // Processing the feed is by far the most memory-intensive operation
  // during the run time of this application (at the moment), the
  // resident set will likely never be this big again. So we ask libc
  // to return as much memory as possible to the OS.
  malloc_trim(0);
  auto const after_load = chrono::steady_clock::now();
  auto const dur_load = chrono::duration_cast<chrono::milliseconds>(after_load - before_load);
  l.info("Loading situations finished in {}", dur_load);

  auto handler = routemon::api::handler{l, std::move(pub)};
  auto http_server = routemon::srv::server{l, std::move(lsel), std::move(handler)};
  http_server.spawn(ioc);
  ioc.run();

  l.error("I/O context stopped");
  return exit_status::failure;
}

auto main(int argc, char* argv[]) -> int {
  if (argc < 0) {
    std::cout << "Fatal: argument count below zero" << std::endl;
    return EXIT_FAILURE;
  }
  auto res = real_main(std::span{const_cast<char const**>(argv), static_cast<std::size_t>(argc)});
  switch (res) {
  case exit_status::failure:
    return EXIT_FAILURE;
  case exit_status::bad_usage:
    return 2;
  }
}