456 lines
16 KiB
C++
456 lines
16 KiB
C++
#pragma once
|
|
|
|
#include <ada.h>
|
|
#include <ada/url_search_params.h>
|
|
|
|
#include <functional>
|
|
#include <optional>
|
|
#include <string>
|
|
#include <string_view>
|
|
|
|
namespace CesiumUtility {
|
|
|
|
/**
|
|
* @brief A class for parsing and manipulating Uniform Resource Identifiers
|
|
* (URIs).
|
|
*
|
|
* The URI parser supports the [WhatWG URL
|
|
* specification](https://url.spec.whatwg.org/). It also supports
|
|
* protocol-relative URIs such as `//example.com`, and opaque paths such as
|
|
* `a/b/c`.
|
|
*/
|
|
class Uri final {
|
|
public:
|
|
/**
|
|
* @brief Attempts to create a new Uri by parsing the given string. If the
|
|
* string fails to parse, \ref isValid will return false.
|
|
*
|
|
* @param uri A string containing the URI to attempt to parse.
|
|
*/
|
|
Uri(const std::string& uri);
|
|
/**
|
|
* @brief Attempts to create a new Uri from a base URI and a relative URI. If
|
|
* the base URI is invalid, only the relative URI string will be used. If the
|
|
* relative URI fails to parse, \ref isValid will return false.
|
|
*
|
|
* @param base The base URI that the relative URI is relative to.
|
|
* @param relative A string containing a relative URI to attempt to parse.
|
|
* @param useBaseQuery If true, the resulting URI will include the query
|
|
* parameters of both the base URI and the relative URI. If false, only the
|
|
* relative URI's query parameters will be used (if any).
|
|
*/
|
|
Uri(const Uri& base, const std::string& relative, bool useBaseQuery = false);
|
|
|
|
/**
|
|
* @brief Returns a string representation of the entire URI including path and
|
|
* query parameters.
|
|
*/
|
|
std::string_view toString() const;
|
|
|
|
/**
|
|
* @brief Returns true if this URI has been successfully parsed.
|
|
*/
|
|
bool isValid() const;
|
|
|
|
/**
|
|
* @brief Equivalent to \ref isValid.
|
|
*/
|
|
operator bool() const { return this->isValid(); }
|
|
|
|
/**
|
|
* @brief Gets the scheme portion of the URI. If the URI was created without a
|
|
* scheme, this will return an empty string.
|
|
*
|
|
* @returns The scheme, or an empty string if the URI could not be parsed or
|
|
* has no scheme.
|
|
*/
|
|
std::string_view getScheme() const;
|
|
|
|
/**
|
|
* @brief Gets the host portion of the URI. If the URI also specifies a
|
|
* non-default port, it will be included in the returned host. If the URI
|
|
* contains no host, this will return an empty string.
|
|
*
|
|
* @returns The host, or an empty string if the URI could not be parsed or has
|
|
* no host.
|
|
*/
|
|
std::string_view getHost() const;
|
|
|
|
/**
|
|
* @brief Gets the path portion of the URI. This will not include query
|
|
* parameters, if present.
|
|
*
|
|
* @return The path, or empty string if the URI could not be parsed.
|
|
*/
|
|
std::string_view getPath() const;
|
|
|
|
/**
|
|
* @brief Returns the filename portion of the URI.
|
|
*
|
|
* For example, for the URI `http://example.com/file.txt`, this will return
|
|
* `file.txt`.
|
|
*
|
|
* @return The filename, or empty string if the URI could not be parsed.
|
|
*/
|
|
std::string_view getFileName() const;
|
|
|
|
/**
|
|
* @brief Returns the filename portion of the URI without any extension.
|
|
*
|
|
* For example, for the URI `http://example.com/file.txt`, this will return
|
|
* `file`.
|
|
*
|
|
* @return The stem, or empty string if the URI could not be parsed.
|
|
*/
|
|
std::string_view getStem() const;
|
|
|
|
/**
|
|
* @brief Returns the extension portion of the URI, if present.
|
|
*
|
|
* For example, for the URI `http://example.com/file.txt`, this will return
|
|
* `.txt`.
|
|
*
|
|
* @return The extension, or empty string if the URI could not be parsed.
|
|
*/
|
|
std::string_view getExtension() const;
|
|
|
|
/**
|
|
* @brief Gets the query portion of the URI.
|
|
*
|
|
* @return The path, or empty string if the URI could not be parsed.
|
|
*/
|
|
std::string_view getQuery() const;
|
|
|
|
/**
|
|
* @brief Sets the path portion of a URI to a new value. The other portions of
|
|
* the URI are left unmodified, including any query parameters.
|
|
*
|
|
* @param path The new path portion of the URI.
|
|
*/
|
|
void setPath(const std::string_view& path);
|
|
|
|
/**
|
|
* @brief Sets the query portion of a URI to a new value. The other portions
|
|
* of the URI are left unmodified.
|
|
*
|
|
* @param queryString The new query portion of the URI.
|
|
*/
|
|
void setQuery(const std::string_view& queryString);
|
|
|
|
/**
|
|
* @brief Attempts to resolve a relative URI using a base URI.
|
|
*
|
|
* For example, a relative URI `/v1/example` together with the base URI
|
|
* `https://api.cesium.com` would resolve to
|
|
* `https://api.cesium.com/v1/example`.
|
|
*
|
|
* @param base The base URI that the relative URI is relative to.
|
|
* @param relative The relative URI to be resolved against the base URI.
|
|
* @param useBaseQuery If true, any query parameters of the base URI will be
|
|
* retained in the resolved URI.
|
|
* @param assumeHttpsDefault This parameter is ignored and is only kept for
|
|
* API compatibility.
|
|
* @returns The resolved URI.
|
|
*
|
|
* @deprecated Use the \ref Uri constructor instead.
|
|
*/
|
|
static std::string resolve(
|
|
const std::string& base,
|
|
const std::string& relative,
|
|
bool useBaseQuery = false,
|
|
bool assumeHttpsDefault = true);
|
|
/**
|
|
* @brief Adds the given key and value to the query string of a URI. For
|
|
* example, `addQuery("https://api.cesium.com/v1/example", "key", "value")`
|
|
* would produce the URL `https://api.cesium.com/v1/example?key=value`.
|
|
*
|
|
* @param uri The URI whose query string will be modified.
|
|
* @param key The key to be added to the query string.
|
|
* @param value The value to be added to the query string.
|
|
* @returns The modified URI including the new query string parameter.
|
|
*
|
|
* @deprecated Use the \ref UriQuery class:
|
|
* ```
|
|
* Uri parsedUri(uri);
|
|
* UriQuery params(parsedUri);
|
|
* params.setValue(key, value);
|
|
* parsedUri.setQuery(params.toQueryString());
|
|
* ```
|
|
*/
|
|
static std::string addQuery(
|
|
const std::string& uri,
|
|
const std::string& key,
|
|
const std::string& value);
|
|
/**
|
|
* @brief Obtains the value of the given key from the query string of the URI,
|
|
* if possible.
|
|
*
|
|
* If the URI can't be parsed, or the key doesn't exist in the
|
|
* query string, an empty string will be returned.
|
|
*
|
|
* @param uri The URI with a query string to obtain a value from.
|
|
* @param key The key whose value will be obtained from the URI, if possible.
|
|
* @returns The value of the given key in the query string, or an empty string
|
|
* if not found.
|
|
*
|
|
* @deprecated Use the \ref UriQuery class:
|
|
* ```
|
|
* Uri parsedUri(uri);
|
|
* UriQuery params(parsedUri);
|
|
* params.getValue(key);
|
|
* ```
|
|
*/
|
|
static std::string
|
|
getQueryValue(const std::string& uri, const std::string& key);
|
|
|
|
/**
|
|
* @brief A callback to fill-in a placeholder value in a URL.
|
|
*
|
|
* @param placeholder The text of the placeholder. For example, if the
|
|
* placeholder was `{example}`, the value of `placeholder` will be `example`.
|
|
* @returns The value to use in place of the placeholder.
|
|
*/
|
|
typedef std::string
|
|
SubstitutionCallbackSignature(const std::string& placeholder);
|
|
|
|
/**
|
|
* @brief Substitutes the placeholders in a templated URI with their
|
|
* appropriate values obtained using a specified callback function.
|
|
*
|
|
* A templated URI has placeholders in the form of `{name}`. For example,
|
|
* `https://example.com/{x}/{y}/{z}` has three placeholders, `x`, `y`, and `z`.
|
|
* The callback will be called for each placeholder and they will be replaced
|
|
* with the return value of this callback.
|
|
*
|
|
* @param templateUri The templated URI whose placeholders will be substituted
|
|
* by this method.
|
|
* @param substitutionCallback The callback that will be called for each
|
|
* placeholder in the provided URI. See \ref SubstitutionCallbackSignature.
|
|
*/
|
|
static std::string substituteTemplateParameters(
|
|
const std::string& templateUri,
|
|
const std::function<SubstitutionCallbackSignature>& substitutionCallback);
|
|
|
|
/**
|
|
* @brief Escapes a portion of a URI, percent-encoding disallowed characters.
|
|
*
|
|
* @param s The string to escape.
|
|
* @return The escaped string.
|
|
*/
|
|
static std::string escape(const std::string& s);
|
|
|
|
/**
|
|
* @brief Unescapes a portion of a URI, decoding any percent-encoded
|
|
* characters.
|
|
*
|
|
* @param s The string to unescape.
|
|
* @return The unescaped string.
|
|
*/
|
|
static std::string unescape(const std::string& s);
|
|
|
|
/**
|
|
* @brief Converts a Unix file system path to a string suitable for use as the
|
|
* path portion of a URI. Characters that are not allowed in the path portion
|
|
* of a URI are percent-encoded as necessary.
|
|
*
|
|
* If the path is absolute (it starts with a slash), then the URI will start
|
|
* with a slash as well.
|
|
*
|
|
* @param unixPath The file system path.
|
|
* @return The URI path.
|
|
*/
|
|
static std::string unixPathToUriPath(const std::string& unixPath);
|
|
|
|
/**
|
|
* @brief Converts a Windows file system path to a string suitable for use as
|
|
* the path portion of a URI. Characters that are not allowed in the path
|
|
* portion of a URI are percent-encoded as necessary.
|
|
*
|
|
* Either `/` or `\` may be used as a path separator on input, but the output
|
|
* will contain only `/`.
|
|
*
|
|
* If the path is absolute (it starts with a slash or with C:\ or similar),
|
|
* then the URI will start with a slash well.
|
|
*
|
|
* @param windowsPath The file system path.
|
|
* @return The URI path.
|
|
*/
|
|
static std::string windowsPathToUriPath(const std::string& windowsPath);
|
|
|
|
/**
|
|
* @brief Converts a file system path on the current system to a string
|
|
* suitable for use as the path portion of a URI. Characters that are not
|
|
* allowed in the path portion of a URI are percent-encoded as necessary.
|
|
*
|
|
* If the `_WIN32` preprocessor definition is defined when compiling
|
|
* cesium-native, this is assumed to be a Windows-like system and this
|
|
* function calls {@link windowsPathToUriPath}. Otherwise, this is assumed to
|
|
* be a Unix-like system and this function calls {@link unixPathToUriPath}.
|
|
*
|
|
* @param nativePath The file system path.
|
|
* @return The URI path.
|
|
*/
|
|
static std::string nativePathToUriPath(const std::string& nativePath);
|
|
|
|
/**
|
|
* @brief Converts the path portion of a URI to a Unix file system path.
|
|
* Percent-encoded characters in the URI are decoded.
|
|
*
|
|
* If the URI path is absolute (it starts with a slash), then the file system
|
|
* path will start with a slash as well.
|
|
*
|
|
* @param uriPath The URI path.
|
|
* @return The file system path.
|
|
*/
|
|
static std::string uriPathToUnixPath(const std::string& uriPath);
|
|
|
|
/**
|
|
* @brief Converts the path portion of a URI to a Windows file system path.
|
|
* Percent-encoded characters in the URI are decoded.
|
|
*
|
|
* If the URI path is absolute (it starts with a slash), then the file system
|
|
* path will start with a slash or a drive letter.
|
|
*
|
|
* @param uriPath The URI path.
|
|
* @return The file system path.
|
|
*/
|
|
static std::string uriPathToWindowsPath(const std::string& uriPath);
|
|
|
|
/**
|
|
* @brief Converts the path portion of a URI to a file system path on the
|
|
* current system. Percent-encoded characters in the URI are decoded.
|
|
*
|
|
* If the `_WIN32` preprocessor definition is defined when compiling
|
|
* cesium-native, this is assumed to be a Windows-like system and this
|
|
* function calls {@link uriPathToWindowsPath}. Otherwise, this is assumed to
|
|
* be a Unix-like system and this function calls {@link uriPathToUnixPath}.
|
|
*
|
|
* @param uriPath The URI path.
|
|
* @return The file system path.
|
|
*/
|
|
static std::string uriPathToNativePath(const std::string& uriPath);
|
|
|
|
/**
|
|
* @brief Gets the path portion of the URI. This will not include path
|
|
* parameters, if present.
|
|
*
|
|
* @param uri The URI from which to get the path.
|
|
* @return The path, or empty string if the URI could not be parsed.
|
|
*
|
|
* @deprecated Create a \ref Uri instance and use \ref Uri::getPath() instead.
|
|
*/
|
|
static std::string getPath(const std::string& uri);
|
|
|
|
/**
|
|
* @brief Sets the path portion of a URI to a new value. The other portions of
|
|
* the URI are left unmodified, including any path parameters.
|
|
*
|
|
* @param uri The URI for which to set the path.
|
|
* @param newPath The new path portion of the URI.
|
|
* @returns The new URI after setting the path. If the original URI cannot be
|
|
* parsed, it is returned unmodified.
|
|
*
|
|
* @deprecated Create a \ref Uri instance and use \ref Uri::setPath(const
|
|
* std::string_view&) instead.
|
|
*/
|
|
static std::string
|
|
setPath(const std::string& uri, const std::string& newPath);
|
|
|
|
/**
|
|
* @brief Ensures that the Uri's path ends with a slash, modifying itself if
|
|
* necessary. Useful when the Uri is used as a base URL.
|
|
*/
|
|
void ensureTrailingSlash();
|
|
|
|
private:
|
|
std::optional<ada::url_aggregator> _url = std::nullopt;
|
|
bool _hasScheme = false;
|
|
};
|
|
|
|
/**
|
|
* @brief A class for parsing and manipulating the query string of a URI.
|
|
*/
|
|
class UriQuery final {
|
|
public:
|
|
/**
|
|
* @brief Creates a \ref UriQuery object from a query string.
|
|
*
|
|
* This query string should be in the format
|
|
* `key1=value1&key2=value2&key3=value3...`. This is the format returned by
|
|
* \ref Uri::getQuery. This string can include percent-encoded values.
|
|
*
|
|
* @param queryString The query string to parse into a query params object.
|
|
*/
|
|
UriQuery(const std::string_view& queryString) : _params(queryString) {}
|
|
/**
|
|
* @brief Creates a \ref UriQuery object from a \ref Uri instance.
|
|
*
|
|
* This is equivalent to `UriQuery(uri.getQuery())`.
|
|
*
|
|
* @param uri The URI instance to obtain the query params from.
|
|
*/
|
|
UriQuery(const Uri& uri) : _params(uri.getQuery()) {}
|
|
/**
|
|
* @brief Creates an empty \ref UriQuery object.
|
|
*/
|
|
UriQuery() = default;
|
|
|
|
/**
|
|
* @brief Obtains the value of the given key from the query parameters,
|
|
* if possible.
|
|
*
|
|
* If the URI can't be parsed, or the key doesn't exist in the
|
|
* query string, `std::nullopt` will be returned.
|
|
*
|
|
* @param key The key whose value will be obtained from the query string, if
|
|
* possible.
|
|
* @returns The value of the given key in the query string, or `std::nullopt`
|
|
* if not found.
|
|
*/
|
|
std::optional<std::string_view> getValue(const std::string& key) {
|
|
return this->_params.get(key);
|
|
}
|
|
|
|
/**
|
|
* @brief Sets the given key in the query parameters to the given value.
|
|
* If the key doesn't exist already, it will be added to the query parameters.
|
|
* Otherwise, the previous value will be overwritten.
|
|
*
|
|
* @param key The key to be added to the query string.
|
|
* @param value The value to be added to the query string.
|
|
*/
|
|
void setValue(const std::string& key, const std::string& value) {
|
|
this->_params.set(key, value);
|
|
}
|
|
|
|
/**
|
|
* @brief Returns true if this query string contains a value for the given
|
|
* key, or false otherwise.
|
|
*
|
|
* @param key The key to check.
|
|
*/
|
|
bool hasValue(const std::string& key) { return this->_params.has(key); }
|
|
|
|
/**
|
|
* @brief Converts this object back into a query string, including all
|
|
* modifications that have been made. This result can be passed directly to
|
|
* \ref Uri::setQuery.
|
|
*/
|
|
std::string toQueryString() const { return this->_params.to_string(); }
|
|
|
|
/** @brief Returns an iterator pointing to the beginning of the query
|
|
* parameters. */
|
|
inline auto begin() const { return this->_params.begin(); }
|
|
/** @brief Returns an iterator pointing to the end of the query parameters. */
|
|
inline auto end() const { return this->_params.end(); }
|
|
/** @brief Returns the first element in the query parameters. */
|
|
inline auto front() const { return this->_params.front(); }
|
|
/** @brief Returns the last element in the query parameters. */
|
|
inline auto back() const { return this->_params.back(); }
|
|
|
|
private:
|
|
ada::url_search_params _params;
|
|
};
|
|
|
|
} // namespace CesiumUtility
|