libcruft-util/uri.hpp

177 lines
5.8 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2015, 2017, 2021 Danny Robson <danny@nerdcruft.net>
*/
#pragma once
#include "debug/assert.hpp"
#include "view.hpp"
#include <fmt/core.h>
#include <array>
#include <iosfwd>
#include <map>
#include <stdexcept>
#include <string>
#include <string_view>
namespace cruft {
// parsing of rfc3986 uniform resource identifiers
//
// does not currently perform normalisation (scheme or protocol),
// comparison, or other associated operations. though these should be
// added in the future.
//
// note that the parsed results may not always conform to expectations
// for some protocols. eg, mailto identifiers are complex to parse
// reliably and would require a specialised parser to be reliable.
//
// not all fields will be present for all protocols (or all instances of
// any given protocol). eg, the "tel" is unlikely to have port numbers.
class uri {
public:
explicit uri (std::string &&);
uri (uri &&) noexcept = default;
uri& operator= (uri &&) noexcept = default;
uri (uri const&) = default;
uri& operator= (uri const&) = default;
explicit uri (const std::string&);
explicit uri (const char *);
explicit uri (view<const char *>);
uri (
std::string_view scheme,
std::string_view authority,
std::string_view path,
std::string_view query,
std::string_view fragment
);
class parse_error : public std::runtime_error
{ using runtime_error::runtime_error; };
// URI: 'https://user:password@example.com:80/path/to?foo=bar#fragment'
//
// SCHEME: 'https'
// HIERARCHICAL: 'user:password@example.com:80/path/to'
// AUTHORITY: 'user:password@example.com:80'
// USER: 'user:password'
// HOST: 'example.com'
// PORT: '80'
// PATH: '/path/to'
// QUERY: 'foo=bar'
// FRAGMENT: 'fragment'
enum component {
/* 0 */ SCHEME,
/* 1 */ USER,
/* 2 */ HOST,
/* 3 */ PORT,
/* 4 */ PATH,
/* 5 */ QUERY,
/* 6 */ FRAGMENT,
NUM_COMPONENTS
};
std::string_view
get (component c) const&;
std::string_view all (void) const& { return m_value; }
std::string const& value (void) const& { return m_value; }
std::string_view scheme (void) const& { return get (SCHEME); }
std::string_view user (void) const& { return get (USER); }
std::string_view host (void) const& { return get (HOST); }
std::string_view port (void) const& { return get (PORT); }
std::string_view path (void) const& { return get (PATH); }
std::string_view query (void) const& { return get (QUERY); }
std::string_view fragment (void) const& { return get (FRAGMENT); }
std::string_view heirarchical (void) const&; //{ return { user ().begin (), path ().end () }; }
std::string_view authority (void) const&; //{ return { user ().begin (), port ().end () }; }
/// Returns a view over the path and query components
std::string_view
pq (void) const&
{
return {
m_value.data () + m_offsets[PATH].first,
m_value.data () + m_offsets[QUERY].second
};
}
/// Returns a view over the path, query, and fragment components
std::string_view
pqf (void) const& {
return {
m_value.data () + m_offsets[PATH].first,
m_value.data () + m_offsets[FRAGMENT].second
};
}
void set (component c, std::string_view val);
// void clear (component);
void clear_fragment (void);
std::array<std::string_view, NUM_COMPONENTS>
components (void) const& noexcept;
static std::string percent_decode (view<const char*>);
private:
void parse (void);
std::array<std::pair<int, int>, NUM_COMPONENTS> m_offsets;
std::string m_value;
};
/// Break a query string into a sequence of key-value pairs.
/// Duplicates are possible.
/// vector is used to that order can be preserved
/// The is the inverse of vector_to_query
std::vector<std::pair<std::string, std::string>>
query_to_vector (std::string_view);
/// Convert a sequence of key-value pairs into a query string
/// All pairs, including duplicates, are serialised in the provided order.
/// This is the inverse of query_to_vector
std::string
vector_to_query (std::vector<std::pair<std::string, std::string>> const&);
bool operator== (uri const&, uri const&) noexcept;
cruft::uri resolve (cruft::uri const &base, cruft::uri const &child);
cruft::uri normalise (cruft::uri const &);
std::ostream& operator<< (std::ostream&, uri const&);
std::ostream& operator<< (std::ostream&, uri::component);
}
// Don't use std::string_view here, fmtlib reimplements it as fmt::string_view.
template <>
struct fmt::formatter<cruft::uri> : public fmt::formatter<string_view> {
format_context::iterator
format (cruft::uri const&, format_context &ctx) const;
};
// Don't use std::string_view here, fmtlib reimplements it as fmt::string_view.
template <> struct fmt::formatter<cruft::uri::component>: formatter<string_view> {
// parse is inherited from formatter<string_view>.
fmt::format_context::iterator
format(cruft::uri::component c, format_context& ctx) const;
};