tomlplusplus/include/toml++/impl/path.h
Mark Gillard 4b166b69f2 v3.2.0
2022-08-30 00:04:04 +03:00

852 lines
22 KiB
C++

//# This file is a part of toml++ and is subject to the the terms of the MIT license.
//# Copyright (c) Mark Gillard <mark.gillard@outlook.com.au>
//# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text.
// SPDX-License-Identifier: MIT
#pragma once
#include "forward_declarations.h"
#include "std_vector.h"
#include "header_start.h"
TOML_NAMESPACE_START
{
/// \brief Indicates type of path component, either a key, an index in an array, or invalid
enum class TOML_CLOSED_ENUM path_component_type : uint8_t
{
key = 0x1,
array_index = 0x2
};
/// \brief Represents a single component of a complete 'TOML-path': either a key or an array index
class TOML_EXPORTED_CLASS path_component
{
/// \cond
struct storage_t
{
static constexpr size_t size =
(sizeof(size_t) < sizeof(std::string) ? sizeof(std::string) : sizeof(size_t));
static constexpr size_t align =
(alignof(size_t) < alignof(std::string) ? alignof(std::string) : alignof(size_t));
alignas(align) unsigned char bytes[size];
};
alignas(storage_t::align) mutable storage_t value_storage_;
path_component_type type_;
TOML_PURE_GETTER
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const path_component&, const path_component&) noexcept;
template <typename Type>
TOML_PURE_INLINE_GETTER
static Type* get_as(storage_t& s) noexcept
{
return TOML_LAUNDER(reinterpret_cast<Type*>(s.bytes));
}
static void store_key(std::string_view key, storage_t& storage_)
{
::new (static_cast<void*>(storage_.bytes)) std::string{ key };
}
static void store_index(size_t index, storage_t& storage_) noexcept
{
::new (static_cast<void*>(storage_.bytes)) std::size_t{ index };
}
void destroy() noexcept
{
if (type_ == path_component_type::key)
get_as<std::string>(value_storage_)->~basic_string();
}
TOML_NODISCARD
size_t& index_ref() noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
return *get_as<size_t>(value_storage_);
}
TOML_NODISCARD
std::string& key_ref() noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
return *get_as<std::string>(value_storage_);
}
/// \endcond
public:
/// \brief Default constructor (creates an empty key).
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component();
/// \brief Constructor for a path component that is an array index
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(size_t index) noexcept;
/// \brief Constructor for a path component that is a key string
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(std::string_view key);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Constructor for a path component that is a key string
///
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(std::wstring_view key);
#endif
/// \brief Copy constructor.
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(const path_component& pc);
/// \brief Move constructor.
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
path_component(path_component&& pc) noexcept;
/// \brief Copy-assignment operator.
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(const path_component& rhs);
/// \brief Move-assignment operator.
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(path_component&& rhs) noexcept;
/// \brief Assigns an array index to this path component.
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(size_t new_index) noexcept;
/// \brief Assigns a path key to this path component.
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(std::string_view new_key);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Assigns a path key to this path component.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_EXPORTED_MEMBER_FUNCTION
path_component& operator=(std::wstring_view new_key);
#endif
/// \brief Destructor.
~path_component() noexcept
{
destroy();
}
/// \name Array index accessors
/// \warning It is undefined behaviour to call these functions when the path component does not represent an array index.
/// Check #type() to determine the component's value type.
/// @{
/// \brief Returns the array index (const lvalue overload).
TOML_PURE_GETTER
size_t index() const noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::array_index);
return *get_as<const size_t>(value_storage_);
}
/// \brief Returns the array index (const lvalue).
TOML_PURE_INLINE_GETTER
explicit operator size_t() const noexcept
{
return index();
}
/// @}
/// \name Key accessors
/// \warning It is undefined behaviour to call these functions when the path component does not represent a key.
/// Check #type() to determine the component's value type.
/// @{
/// \brief Returns the key string.
TOML_PURE_GETTER
const std::string& key() const noexcept
{
TOML_ASSERT_ASSUME(type_ == path_component_type::key);
return *get_as<const std::string>(value_storage_);
}
/// \brief Returns the key string.
TOML_PURE_INLINE_GETTER
explicit operator const std::string&() const noexcept
{
return key();
}
/// @}
/// \brief Retrieve the type of this path component, either path_component::key or path_component::array_index
TOML_PURE_INLINE_GETTER
path_component_type type() const noexcept
{
return type_;
}
/// \name Equality
/// @{
/// \brief Returns true if two path components represent the same key or array index.
TOML_PURE_INLINE_GETTER
friend bool operator==(const path_component& lhs, const path_component& rhs) noexcept
{
return equal(lhs, rhs);
}
/// \brief Returns true if two path components do not represent the same key or array index.
TOML_PURE_INLINE_GETTER
friend bool operator!=(const path_component& lhs, const path_component& rhs) noexcept
{
return !equal(lhs, rhs);
}
/// @}
};
/// \brief A TOML path.
///
/// \detail This type parses and represents a path to a TOML node. It validates
/// the syntax of the path but does not ensure that the path refers to
/// a valid node in any particular TOML document. If parsing fails,
/// the object will evaluate as 'falsy', and will be empty.
///
/// \cpp
/// toml::path the_path("animals.cats[1]");
///
/// // can use with tbl.at_path or operator[]
/// std::cout << "second cat: " << tbl[the_path] << "\n";
/// std::cout << "cats: " << tbl.at_path(the_path.parent_path()) << "\n";
/// \ecpp
///
/// \out
/// second cat: lion
/// cats: ['tiger', 'lion', 'puma']
/// \eout
class TOML_EXPORTED_CLASS path
{
private:
/// \cond
std::vector<path_component> components_;
TOML_EXPORTED_MEMBER_FUNCTION
void print_to(std::ostream&) const;
TOML_PURE_GETTER
TOML_EXPORTED_STATIC_FUNCTION
static bool TOML_CALLCONV equal(const path&, const path&) noexcept;
/// \endcond
public:
/// \brief Default constructor.
TOML_NODISCARD_CTOR
path() noexcept = default;
/// \brief Construct a path by parsing from a string.
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
explicit path(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Construct a path by parsing from a string.
///
/// \availability This constructor is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD_CTOR
TOML_EXPORTED_MEMBER_FUNCTION
explicit path(std::wstring_view);
#endif
/// \brief Default destructor.
~path() noexcept = default;
/// \brief Copy constructor.
TOML_NODISCARD_CTOR
path(const path&) = default;
/// \brief Move constructor.
TOML_NODISCARD_CTOR
path(path&&) noexcept = default;
/// \brief Returns the number of components in the path.
TOML_PURE_INLINE_GETTER
size_t size() const noexcept
{
return components_.size();
}
/// \brief Returns true if the path has one or more components.
TOML_PURE_INLINE_GETTER
explicit operator bool() const noexcept
{
return !components_.empty();
}
/// \brief Whether (true) or not (false) the path is empty
TOML_PURE_INLINE_GETTER
bool empty() const noexcept
{
return components_.empty();
}
/// \brief Fetch a path component by index.
TOML_PURE_INLINE_GETTER
path_component& operator[](size_t index) noexcept
{
TOML_ASSERT(index < size());
return components_[index];
}
/// \brief Fetch a path component by index (const overload).
TOML_PURE_INLINE_GETTER
const path_component& operator[](size_t index) const noexcept
{
TOML_ASSERT(index < size());
return components_[index];
}
/// \name Assignment
/// @{
/// \brief Copy-assignment operator.
path& operator=(const path&) = default;
/// \brief Move-assignment operator.
path& operator=(path&&) noexcept = default;
/// \brief Replaces the contents of the path by parsing from a string.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator=(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Replaces the contents of the path by parsing from a string.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator=(std::wstring_view);
#endif
/// \brief Replaces the contents of the path with that of another.
TOML_ALWAYS_INLINE
path& assign(const path& p)
{
return *this = p;
}
/// \brief Replaces the contents of the path with that of another.
TOML_ALWAYS_INLINE
path& assign(path&& p) noexcept
{
return *this = std::move(p);
}
/// \brief Replaces the contents of the path object by a new path
TOML_ALWAYS_INLINE
path& assign(std::string_view str)
{
return *this = str;
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Replaces the contents of the path object by a new path
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_ALWAYS_INLINE
path& assign(std::wstring_view str)
{
return *this = str;
}
#endif
/// @}
/// \name Appending
/// @{
/// \brief Appends another path onto the end of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(const path&);
/// \brief Appends another path onto the end of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(path&&);
/// \brief Parses a path and appends it onto the end of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Parses a path and appends it onto the end of this one.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_EXPORTED_MEMBER_FUNCTION
path& operator+=(std::wstring_view);
#endif
/// \brief Appends another path onto the end of this one.
TOML_ALWAYS_INLINE
path& append(const path& p)
{
return *this += p;
}
/// \brief Appends another path onto the end of this one.
TOML_ALWAYS_INLINE
path& append(path&& p)
{
return *this += std::move(p);
}
/// \brief Parses a path and appends it onto the end of this one.
TOML_ALWAYS_INLINE
path& append(std::string_view str)
{
return *this += str;
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Parses a path and appends it onto the end of this one.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_ALWAYS_INLINE
path& append(std::wstring_view str)
{
return *this += str;
}
#endif
/// @}
/// \name Prepending
/// @{
/// \brief Prepends another path onto the beginning of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(const path&);
/// \brief Prepends another path onto the beginning of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(path&&);
/// \brief Parses a path and prepends it onto the beginning of this one.
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(std::string_view);
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Parses a path and prepends it onto the beginning of this one.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_EXPORTED_MEMBER_FUNCTION
path& prepend(std::wstring_view);
#endif
/// @}
/// \name Concatenation
/// @{
/// \brief Concatenates two paths.
TOML_NODISCARD
friend path operator+(const path& lhs, const path& rhs)
{
path result = lhs;
result += rhs;
return result;
}
/// \brief Concatenates two paths.
TOML_NODISCARD
friend path operator+(const path& lhs, std::string_view rhs)
{
path result = lhs;
result += rhs;
return result;
}
/// \brief Concatenates two paths.
TOML_NODISCARD
friend path operator+(std::string_view lhs, const path& rhs)
{
path result = rhs;
result.prepend(lhs);
return result;
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Concatenates two paths.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
friend path operator+(const path& lhs, std::wstring_view rhs)
{
path result = lhs;
result += rhs;
return result;
}
/// \brief Concatenates two paths.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
friend path operator+(std::wstring_view lhs, const path& rhs)
{
path result = rhs;
result.prepend(lhs);
return result;
}
#endif
/// @}
/// \name String conversion
/// @{
/// \brief Prints the string representation of a #toml::path out to a stream.
TOML_ALWAYS_INLINE
friend std::ostream& operator<<(std::ostream& os, const path& rhs)
{
rhs.print_to(os);
return os;
}
/// \brief Returns a string representation of this path.
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
std::string str() const;
/// \brief Returns a string representation of this path.
TOML_NODISCARD
TOML_ALWAYS_INLINE
explicit operator std::string() const
{
return str();
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Returns a string representation of this path.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
std::wstring wide_str() const;
/// \brief Returns a string representation of this path.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_ALWAYS_INLINE
explicit operator std::wstring() const
{
return wide_str();
}
#endif
/// @}
/// \name Equality
/// @{
/// \brief Returns whether two paths are the same.
TOML_PURE_INLINE_GETTER
friend bool operator==(const path& lhs, const path& rhs) noexcept
{
return equal(lhs, rhs);
}
/// \brief Returns whether two paths are not the same.
TOML_PURE_INLINE_GETTER
friend bool operator!=(const path& lhs, const path& rhs) noexcept
{
return !equal(lhs, rhs);
}
/// \brief Returns whether two paths are the same.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(const path& lhs, std::string_view rhs)
{
return lhs == path{ rhs };
}
/// \brief Returns whether two paths are the same.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(std::string_view lhs, const path& rhs)
{
return rhs == lhs;
}
/// \brief Returns whether two paths are not the same.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(const path& lhs, std::string_view rhs)
{
return lhs != path{ rhs };
}
/// \brief Returns whether two paths are not the same.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(std::string_view lhs, const path& rhs)
{
return rhs != lhs;
}
#if TOML_ENABLE_WINDOWS_COMPAT
/// \brief Returns whether two paths are the same.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(const path& lhs, std::wstring_view rhs)
{
return lhs == path{ rhs };
}
/// \brief Returns whether two paths are the same.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator==(std::wstring_view lhs, const path& rhs)
{
return rhs == lhs;
}
/// \brief Returns whether two paths are not the same.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(const path& lhs, std::wstring_view rhs)
{
return lhs != path{ rhs };
}
/// \brief Returns whether two paths are not the same.
///
/// \availability This overload is only available when #TOML_ENABLE_WINDOWS_COMPAT is enabled.
TOML_NODISCARD
TOML_ALWAYS_INLINE
friend bool operator!=(std::wstring_view lhs, const path& rhs)
{
return rhs != lhs;
}
#endif // TOML_ENABLE_WINDOWS_COMPAT
/// @}
/// \name Iteration
/// @{
/// An iterator for iterating over the components in the path.
/// \see #toml::path_component
using iterator = std::vector<path_component>::iterator;
/// A const iterator for iterating over the components in the path.
/// \see #toml::path_component
using const_iterator = std::vector<path_component>::const_iterator;
/// \brief Returns an iterator to the first component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
iterator begin() noexcept
{
return components_.begin();
}
/// \brief Returns an iterator to one-past-the-last component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
iterator end() noexcept
{
return components_.end();
}
/// \brief Returns a const iterator to the first component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
const_iterator begin() const noexcept
{
return components_.begin();
}
/// \brief Returns a const iterator to one-past-the-last component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
const_iterator end() const noexcept
{
return components_.end();
}
/// \brief Returns a const iterator to the first component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
const_iterator cbegin() const noexcept
{
return components_.begin();
}
/// \brief Returns a const iterator to one-past-the-last component in the path.
/// \see #toml::path_component
TOML_PURE_INLINE_GETTER
const_iterator cend() const noexcept
{
return components_.end();
}
/// @}
/// \name Subpaths and Truncation
/// @{
/// \brief Erases the contents of the path.
TOML_EXPORTED_MEMBER_FUNCTION
void clear() noexcept;
/// \brief Removes the number of terminal path components specified by n
TOML_EXPORTED_MEMBER_FUNCTION
path& truncate(size_t n);
/// \brief Returns a toml::path object which has had n terminal path components removed
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path truncated(size_t n) const;
/// \brief Returns a toml::path object representing the path of the parent node
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path parent() const;
/// \brief Returns a toml::path object representing terminal n-parts of a TOML path
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path leaf(size_t n = 1) const;
/// \brief Returns a toml::path object that is a specified subpath of the current path, representing the
/// range of path components from [start, end).
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path subpath(const_iterator start, const_iterator end) const;
/// \brief Returns a toml::path object that is a specified subpath of the current path, representing the
/// range of path components with indexes from [start, start + length].
TOML_NODISCARD
TOML_EXPORTED_MEMBER_FUNCTION
path subpath(size_t start, size_t length) const;
/// @}
};
inline namespace literals
{
/// \brief Parses a TOML path from a string literal.
///
/// \detail \cpp
/// using namespace toml::literals;
///
/// auto path = "main.settings.devices[2]"_tpath;
/// std::cout << path.parent_path() << "\n";
/// \ecpp
///
/// \out
/// main.settings.devices
/// \eout
///
/// \param str The string data.
/// \param len The string length.
///
/// \returns A #toml::path generated from the string literal.
TOML_NODISCARD
TOML_ALWAYS_INLINE
path operator"" _tpath(const char* str, size_t len)
{
return path(std::string_view{ str, len });
}
}
/// \brief Returns a view of the node matching a fully-qualified "TOML path".
///
/// \detail \cpp
/// auto config = toml::parse(R"(
///
/// [foo]
/// bar = [ 0, 1, 2, [ 3 ], { kek = 4 } ]
///
/// )"sv);
///
/// toml::path path1("foo.bar[2]");
/// toml::path path2("foo.bar[4].kek");
/// std::cout << toml::at_path(config, path1) << "\n";
/// std::cout << toml::at_path(config, path1.parent_path()) << "\n";
/// std::cout << toml::at_path(config, path2) << "\n";
/// std::cout << toml::at_path(config, path2.parent_path()) << "\n";
/// \ecpp
///
/// \out
/// 2
/// [ 0, 1, 2, [ 3 ], { kek = 4 } ]
/// 4
/// { kek = 4 }
/// \eout
///
///
/// \note Keys in paths are interpreted literally, so whitespace (or lack thereof) matters:
/// \cpp
/// toml::at_path(config, toml::path("foo.bar")) // same as config["foo"]["bar"]
/// toml::at_path(config, toml::path("foo. bar")) // same as config["foo"][" bar"]
/// toml::at_path(config, toml::path("foo..bar")) // same as config["foo"][""]["bar"]
/// toml::at_path(config, toml::path(".foo.bar")) // same as config[""]["foo"]["bar"]
/// \ecpp
/// <br>
/// Additionally, TOML allows '.' (period) characters to appear in keys if they are quoted strings.
/// This function makes no allowance for this, instead treating all period characters as sub-table delimiters.
///
/// \param root The root node from which the path will be traversed.
/// \param path The "TOML path" to traverse.
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<node> TOML_CALLCONV at_path(node & root, const toml::path& path) noexcept;
/// \brief Returns a const view of the node matching a fully-qualified "TOML path".
///
/// \see #toml::at_path(node&, const toml::path& path)
TOML_NODISCARD
TOML_EXPORTED_FREE_FUNCTION
node_view<const node> TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept;
}
TOML_NAMESPACE_END;
#include "header_end.h"