//# This file is a part of toml++ and is subject to the the terms of the MIT license. //# Copyright (c) Mark Gillard //# See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. // SPDX-License-Identifier: MIT #pragma once #include "forward_declarations.hpp" #include "std_vector.hpp" #include "header_start.hpp" 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 TOML_PURE_INLINE_GETTER static Type* get_as(storage_t& s) noexcept { return TOML_LAUNDER(reinterpret_cast(s.bytes)); } static void store_key(std::string_view key, storage_t& storage_) { ::new (static_cast(storage_.bytes)) std::string{ key }; } static void store_index(size_t index, storage_t& storage_) noexcept { ::new (static_cast(storage_.bytes)) std::size_t{ index }; } void destroy() noexcept { if (type_ == path_component_type::key) get_as(value_storage_)->~basic_string(); } TOML_NODISCARD size_t& index_ref() noexcept { TOML_ASSERT_ASSUME(type_ == path_component_type::array_index); return *get_as(value_storage_); } TOML_NODISCARD std::string& key_ref() noexcept { TOML_ASSERT_ASSUME(type_ == path_component_type::key); return *get_as(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(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(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 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::iterator; /// A const iterator for iterating over the components in the path. /// \see #toml::path_component using const_iterator = std::vector::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 ///
/// 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 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 TOML_CALLCONV at_path(const node& root, const toml::path& path) noexcept; } TOML_NAMESPACE_END; #include "header_end.hpp"