#pragma once #include "tl/detail/prologue.h" #include #include "tl/path_system.h" #include "tl/rel_path_view.h" #include "tl/detail/path_view_base.h" #include "tl/plain_crash.h" #include "tl/format.h" namespace tl { template class abs_path; template class abs_path_view; template struct path_view_key_less; ////////////////////////////////////////////////////////////////////////// template class abs_path_view : public detail::path_system::path_view_base { template friend class rel_path_view; public: using rel_path_view_type = rel_path_view; abs_path_view() noexcept; abs_path_view(int pathSystemId, tl::span elements) noexcept; abs_path_view(const abs_path_view& other) noexcept; // operators abs_path_view& operator=(const abs_path_view& other) noexcept; bool operator==(const abs_path_view& other) const noexcept; bool operator!=(const abs_path_view& other) const noexcept; auto operator<=>(const abs_path_view& other) const noexcept; void swap(abs_path_view& other) noexcept; void clear() noexcept; string str() const noexcept; eastl::string eastl_str() const noexcept; std::string std_str() const noexcept; rel_path_view subpath(size_t idx, int count = 0) const noexcept; abs_path_view parent() const noexcept; abs_path_view parent(size_t levels) const noexcept; bool is_prefix_of(const abs_path_view& path) const noexcept; //This only works if `this` is a prefix of `to`, in other words, only if the resultant path can be expressed as a view of the `to` path // This is true when going from a parent to a subfolder //Example: //Good: ("/a/b/c").prefix_path_to("/a/b/c/d") == ("d") //Bad: ("/a/b/c").prefix_path_to("/a/b") == ("") will assert and return empty. It should return ".." but it cannot rel_path_view prefix_path_to(const abs_path_view& to) const noexcept; template bool is() const noexcept; bool is_valid() const noexcept; private: int m_pathSystemId = -1; friend class abs_path; }; ////////////////////////////////////////////////////////////////////////// } ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////// namespace tl { ////////////////////////////////////////////////////////////////////////// template abs_path_view::abs_path_view() noexcept { } ////////////////////////////////////////////////////////////////////////// template abs_path_view::abs_path_view(int pathSystemId, tl::span elements) noexcept : path_view_base(elements) , m_pathSystemId(pathSystemId) { } ////////////////////////////////////////////////////////////////////////// template abs_path_view::abs_path_view(const abs_path_view& other) noexcept : path_view_base(other) , m_pathSystemId(other.m_pathSystemId) { } //////////////////////////////////////////////////////////////////////////////////////////////////////////// template abs_path_view& abs_path_view::operator=(const abs_path_view& other) noexcept { m_elements = other.m_elements; m_hash = other.m_hash; m_pathSystemId = other.m_pathSystemId; return *this; } ////////////////////////////////////////////////////////////////////////// template bool abs_path_view::operator==(const abs_path_view& other) const noexcept { return (!this->m_hash || !other.m_hash || this->m_hash == other.m_hash) && this->m_pathSystemId == other.m_pathSystemId && this->m_elements == other.m_elements; } ////////////////////////////////////////////////////////////////////////// template bool abs_path_view::operator!=(const abs_path_view& other) const noexcept { return !(*this == other); } ////////////////////////////////////////////////////////////////////////// template void abs_path_view::clear() noexcept { m_elements = {}; m_hash = 0; m_pathSystemId = -1; } ////////////////////////////////////////////////////////////////////////// template string abs_path_view::str() const noexcept { if (!is_valid() && this->empty()) return {}; return PathSystems::template format_abs(m_pathSystemId, this->m_elements); } ////////////////////////////////////////////////////////////////////////// template eastl::string abs_path_view::eastl_str() const noexcept { if (!is_valid() && this->empty()) return {}; return PathSystems::template format_abs(m_pathSystemId, this->m_elements); } ////////////////////////////////////////////////////////////////////////// template std::string abs_path_view::std_str() const noexcept { if (!is_valid() && this->empty()) return {}; return PathSystems::template format_abs(m_pathSystemId, this->m_elements); } ////////////////////////////////////////////////////////////////////////// template rel_path_view abs_path_view::subpath(size_t idx, int count /* = 0 */) const noexcept { if (count == 0) count = (int)this->size() - (int)idx; if (count < 0) count = (int)this->size() - (int)idx + count; if (count > 0 && idx < this->size()) return rel_path_view(tl::span(this->m_elements.begin() + idx, this->m_elements.begin() + idx + count)); return {}; } ////////////////////////////////////////////////////////////////////////// template abs_path_view abs_path_view::parent() const noexcept { if (this->empty()) { TL_PLAIN_FAIL("Invalid path"); return {}; } abs_path_view p = *this; p.pop_back(); if (!PathSystems::validate_abs_path(p.m_pathSystemId, p.m_elements)) { TL_PLAIN_FAIL("Invalid path"); return {}; } return p; } ////////////////////////////////////////////////////////////////////////// template abs_path_view abs_path_view::parent(size_t levels) const noexcept { if (this->empty() || this->size() <= levels) { TL_PLAIN_FAIL("Invalid path"); return {}; } abs_path_view p = *this; p.shrink(p.size() - levels); return p; } ////////////////////////////////////////////////////////////////////////// template template bool abs_path_view::is() const noexcept { return PathSystems::template is(m_pathSystemId); } ////////////////////////////////////////////////////////////////////////// template bool abs_path_view::is_valid() const noexcept { return m_pathSystemId >= 0; } ////////////////////////////////////////////////////////////////////////// template bool abs_path_view::is_prefix_of(const abs_path_view& path) const noexcept { if (m_pathSystemId != path.m_pathSystemId) return false; if (this->size() > path.size()) return false; for (size_t i = 0, _count = this->size(); i < _count; ++i) { if ((*this)[i] != path[i]) return false; } return true; } ////////////////////////////////////////////////////////////////////////// template rel_path_view abs_path_view::prefix_path_to(const abs_path_view& to) const noexcept { if (this->empty()) return rel_path_view(to.m_elements); if (!is_prefix_of(to)) { TL_PLAIN_FAIL("Invalid path"); return {}; } return rel_path_view(tl::span(to.m_elements.begin() + size(), to.m_elements.end())); } } ////////////////////////////////////////////////////////////////////////// template struct std::formatter> { constexpr auto parse(format_parse_context& ctx) noexcept { return ctx.begin(); } auto format(const tl::abs_path_view& p, std::format_context& ctx) const { return format_to(ctx.out(), "{}", p.str()); } }; template struct std::hash> { std::size_t operator()(const tl::abs_path_view& p) const { return p.hash(); } }; template struct eastl::hash> { std::size_t operator()(const tl::abs_path_view& p) const { return p.hash(); } };