94 lines
2.8 KiB
C++
94 lines
2.8 KiB
C++
// Copyright (c) 2023, AgiBot Inc.
|
||
// All rights reserved.
|
||
|
||
#pragma once
|
||
|
||
#include <optional>
|
||
#include <regex>
|
||
#include <sstream>
|
||
#include <string>
|
||
#include <string_view>
|
||
|
||
namespace aimrt::common::util {
|
||
|
||
/**
|
||
* @brief url元素
|
||
*
|
||
* @tparam StringType
|
||
*/
|
||
template <class StringType = std::string_view>
|
||
requires(std::is_same_v<StringType, std::string_view> ||
|
||
std::is_same_v<StringType, std::string>)
|
||
struct Url {
|
||
StringType protocol; // 协议
|
||
StringType host; // host
|
||
StringType service; // 端口
|
||
StringType path; // 路径
|
||
StringType query; // 参数
|
||
StringType fragment; // 额外信息
|
||
};
|
||
|
||
/**
|
||
* @brief url解析
|
||
* @note url结构:[protocol://][host][:service][path][?query][#fragment]
|
||
* @param url_str url字符串
|
||
* @return std::optional<UrlView> url结构,nullopt则代表解析失败
|
||
*/
|
||
template <class StringType = std::string_view>
|
||
std::optional<Url<StringType>> ParseUrl(std::string_view url_str) {
|
||
std::regex url_regex(
|
||
R"(^(([^:\/?#]+)://)?(([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)",
|
||
std::regex::ECMAScript);
|
||
std::match_results<std::string_view::const_iterator> url_match_result;
|
||
|
||
if (!std::regex_match(url_str.begin(), url_str.end(), url_match_result, url_regex))
|
||
return std::nullopt;
|
||
|
||
Url<StringType> url;
|
||
if (url_match_result[2].matched)
|
||
url.protocol =
|
||
StringType(url_match_result[2].first, url_match_result[2].second);
|
||
if (url_match_result[4].matched) {
|
||
StringType auth(url_match_result[4].first, url_match_result[4].second);
|
||
size_t pos = auth.find_first_of(':');
|
||
if (pos != StringType::npos) {
|
||
url.host = auth.substr(0, pos);
|
||
url.service = auth.substr(pos + 1);
|
||
} else {
|
||
url.host = auth;
|
||
}
|
||
}
|
||
if (url_match_result[5].matched)
|
||
url.path = StringType(url_match_result[5].first, url_match_result[5].second);
|
||
if (url_match_result[7].matched)
|
||
url.query = StringType(url_match_result[7].first, url_match_result[7].second);
|
||
if (url_match_result[9].matched)
|
||
url.fragment = StringType(url_match_result[9].first, url_match_result[9].second);
|
||
|
||
return std::optional<Url<StringType>>{url};
|
||
}
|
||
|
||
/**
|
||
* @brief url拼接
|
||
* @note url结构:[protocol://][host][:service][path][?query][#fragment]
|
||
* @param url url结构
|
||
* @return std::string url字符串
|
||
*/
|
||
template <class StringType = std::string_view>
|
||
std::string JoinUrl(const Url<StringType>& url) {
|
||
std::stringstream ss;
|
||
|
||
if (!url.protocol.empty()) ss << url.protocol << "://";
|
||
if (!url.host.empty()) ss << url.host;
|
||
if (!url.service.empty()) ss << ":" << url.service;
|
||
if (!url.path.empty()) {
|
||
if (url.path[0] != '/') ss << '/';
|
||
ss << url.path;
|
||
}
|
||
if (!url.query.empty()) ss << '?' << url.query;
|
||
if (!url.fragment.empty()) ss << '#' << url.fragment;
|
||
|
||
return ss.str();
|
||
}
|
||
|
||
} // namespace aimrt::common::util
|