#pragma once #include "../common/adc_traits.h" namespace adc { /* * Very simple URL parser and holder class * * URL: proto_mark://host_name:port_num/path */ class AdcURL { protected: static constexpr std::string_view protoHostDelim = "://"; static constexpr std::string_view hostPortDelim = ":"; static constexpr std::string_view portPathDelim = "/"; public: enum proto_id_t { PROTO_ID_LOCAL, PROTO_ID_TCP, PROTO_ID_TLS, PROTO_ID_UDP, PROTO_ID_WS, PROTO_ID_WSS }; static constexpr std::string_view protoMarkLocal{"local"}; // UNIX domain static constexpr std::string_view protoMarkTCP{"tcp"}; // TCP static constexpr std::string_view protoMarkTLS{"tls"}; // TLS static constexpr std::string_view protoMarkUDP{"udp"}; // UDP static constexpr std::string_view protoMarkWS{"ws"}; // Websocket static constexpr std::string_view protoMarkWSS{"wss"}; // Secure Websocket static constexpr std::array validProtoMarks = {protoMarkLocal, protoMarkTCP, protoMarkTLS, protoMarkUDP, protoMarkWS, protoMarkWSS}; /* Constructors and destructor */ AdcURL() = default; // full-feature constructor template AdcURL(proto_id_t proto_id, HT&& host, int port, PTT&& path) : AdcURL() { setURL(proto_id, std::forward(host), port, std::forward(path)); } bool valid() const { // return _isValid; } template R url() const { R r; if (!_isValid) { return r; } if (_protoID == PROTO_ID_LOCAL) { // only proto mark, host and its delimiter std::ranges::copy(_urlView | std::views::take(3) | std::views::join, std::back_inserter(r)); } else { std::ranges::copy(_urlView | std::views::join, std::back_inserter(r)); } return r; } std::string url() const { // return url(); } template R proto() const { if (!_isValid) { return R(); } const auto& proto = validProtoMarks[_protoID]; return R{proto.begin(), proto.end()}; } std::string_view proto() const { if (!_isValid) { return std::string_view(); } return validProtoMarks[_protoID]; } template R host() const { R r; if (!_isValid) { return r; } std::ranges::copy(_host, std::back_inserter(r)); return r; } template R hostView() const { if (!_isValid) { return R(); } return R{_host.begin(), _host.end()}; } std::string host() const { // return _host; } std::string_view hostView() const { // return std::string_view{_host.begin(), _host.end()}; } int port() const { // return _isValid ? _port : -1; } template R path() const { R r; if (!_isValid) { return r; } std::ranges::copy(_path, std::back_inserter(r)); return r; } template R pathView() const { if (!_isValid) { return R(); } return R{_path.begin(), _path.end()}; } std::string path() const { // return _isValid ? _path : ""; } std::string_view pathView() const { // return _isValid ? std::string_view{_path.begin(), _path.end()} : std::string_view(); } template bool setURL(proto_id_t proto_id, HT&& host, int port, PTT&& path) { if (!std::distance(host.begin(), host.end())) { return false; } switch (proto_id) { case PROTO_ID_LOCAL: // ignore path (always "") and port (just skip) _port = -1; _path.clear(); break; case PROTO_ID_TCP: case PROTO_ID_TLS: case PROTO_ID_UDP: case PROTO_ID_WS: case PROTO_ID_WSS: if (port <= 0) { return false; } _port = port; _path.clear(); std::ranges::copy(std::forward(path), std::back_inserter(_path)); break; default: break; } _isValid = true; _protoID = proto_id; _host.clear(); std::ranges::copy(std::forward(host), std::back_inserter(_host)); updateURLView(); return true; } template bool setURL(R&& r) { auto found = std::ranges::search(std::forward(r), protoHostDelim); if (found.empty()) { return false; } return true; } protected: bool _isValid{false}; proto_id_t _protoID; std::string _host, _path, _portStr; int _port; std::array _urlView{protoMarkLocal, protoHostDelim, "localhost", hostPortDelim, "7777", portPathDelim, ""}; void updateURLView() { _urlView[0] = validProtoMarks[_protoID]; _urlView[2] = _host.c_str(); _portStr = std::to_string(_port); _urlView[4] = _port == -1 ? "" : _portStr.c_str(); _urlView[6] = _path.c_str(); } }; } // namespace adc