blob: dbd54becf4ad090876bb5751d16f3eeaddb3ce3f [file]
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RIEGELI_BYTES_PATH_REF_H_
#define RIEGELI_BYTES_PATH_REF_H_
#include <stddef.h>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/attributes.h"
#include "absl/strings/string_view.h"
#include "riegeli/base/compare.h"
#include "riegeli/base/initializer.h"
#include "riegeli/base/invoker.h"
#include "riegeli/base/maker.h"
#include "riegeli/base/string_ref.h"
#include "riegeli/base/temporary_storage.h"
#include "riegeli/base/type_traits.h"
#if defined(__cpp_lib_filesystem) && __cpp_lib_filesystem >= 201703
#include <filesystem>
#endif
namespace riegeli {
// Filename used for default-constructed or moved-from objects.
constexpr char kDefaultFilenameCStr[] = "<none>";
constexpr absl::string_view kDefaultFilename = kDefaultFilenameCStr;
// `PathRef` stores an `absl::string_view` representing a file path.
//
// It is intended for function parameters when the implementation needs
// an `absl::string_view`, and the caller might have another representation
// of the string.
//
// It is convertible from:
// * types convertible to `absl::string_view`
// * types convertible to `std::string`, e.g. `PathInitializer`
// * `std::filesystem::path`
//
// For `std::filesystem::path` with `value_type = char`, it refers to
// `path.native()`.
//
// For `std::filesystem::path` with `value_type = wchar_t`, it refers to
// `path.string()` stored in a storage object passed as a default argument to
// the constructor.
//
// `PathRef` does not own path contents and is efficiently copyable.
class PathRef : public StringRef, public WithCompare<PathRef> {
public:
// Stores an empty `absl::string_view`.
PathRef() = default;
// Stores `str` converted to `absl::string_view`.
ABSL_ATTRIBUTE_ALWAYS_INLINE
/*implicit*/ PathRef(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: StringRef(absl::string_view(str)) {}
// Stores `str` converted to `StringRef` and then to `absl::string_view`.
template <typename T,
std::enable_if_t<
std::conjunction_v<NotSameRef<PathRef, T>,
std::is_convertible<T&&, absl::string_view>>,
int> = 0>
/*implicit*/ PathRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: StringRef(std::forward<T>(str)) {}
// Stores `str` materialized, then converted to `StringRef` and then to
// `absl::string_view`.
template <typename T,
std::enable_if_t<
std::conjunction_v<
NotSameRef<PathRef, T>,
std::negation<std::is_convertible<T&&, absl::string_view>>,
std::is_convertible<T&&, std::string>>,
int> = 0>
/*implicit*/ PathRef(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<std::string>&& storage
ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: StringRef(std::forward<T>(str), std::move(storage)) {}
#if __cpp_lib_filesystem >= 201703
// For `std::filesystem::path` with `value_type = char`, stores a reference to
// `path.native()`.
template <
typename DependentPath = std::filesystem::path,
std::enable_if_t<std::is_same_v<typename DependentPath::value_type, char>,
int> = 0>
/*implicit*/ PathRef(
const std::filesystem::path& path ABSL_ATTRIBUTE_LIFETIME_BOUND)
: StringRef(static_cast<const DependentPath&>(path).native()) {}
template <
typename DependentPath = std::filesystem::path,
std::enable_if_t<std::is_same_v<typename DependentPath::value_type, char>,
int> = 0>
/*implicit*/ PathRef(
const std::filesystem::path& path ABSL_ATTRIBUTE_LIFETIME_BOUND,
ABSL_ATTRIBUTE_UNUSED TemporaryStorage<std::string>&& storage)
: PathRef(path) {}
// For `std::filesystem::path` with `value_type = wchar_t`, stores a reference
// to `path.string()` stored in a storage object passed as a default argument
// to this constructor.
template <
typename DependentPath = std::filesystem::path,
std::enable_if_t<
!std::is_same_v<typename DependentPath::value_type, char>, int> = 0>
/*implicit*/ PathRef(const std::filesystem::path& path,
TemporaryStorage<std::string>&& storage
ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: StringRef(std::move(storage).emplace(
riegeli::Invoker([&path] { return path.string(); }))) {}
#endif
PathRef(const PathRef& that) = default;
PathRef& operator=(const PathRef&) = delete;
friend bool operator==(PathRef a, PathRef b) {
return absl::string_view(a) == absl::string_view(b);
}
friend riegeli::StrongOrdering RIEGELI_COMPARE(PathRef a, PathRef b) {
return riegeli::Compare(absl::string_view(a), absl::string_view(b));
}
template <
typename T,
std::enable_if_t<std::conjunction_v<NotSameRef<PathRef, T>,
std::is_convertible<T&&, StringRef>>,
int> = 0>
friend bool operator==(PathRef a, T&& b) {
return a == PathRef(std::forward<T>(b));
}
template <
typename T,
std::enable_if_t<std::conjunction_v<NotSameRef<PathRef, T>,
std::is_convertible<T&&, StringRef>>,
int> = 0>
friend riegeli::StrongOrdering RIEGELI_COMPARE(PathRef a, T&& b) {
return riegeli::Compare(a, PathRef(std::forward<T>(b)));
}
#if __cpp_lib_filesystem >= 201703
friend bool operator==(PathRef a, const std::filesystem::path& b) {
return a == PathRef(b);
}
friend riegeli::StrongOrdering RIEGELI_COMPARE(
PathRef a, const std::filesystem::path& b) {
return riegeli::Compare(a, PathRef(b));
}
#endif
};
// `PathInitializer` is convertible from the same types as `PathRef`,
// but efficiently takes ownership of `std::string`.
//
// `PathInitializer` behaves like `Initializer<std::string>`.
class PathInitializer : public Initializer<std::string> {
public:
#if __cpp_lib_filesystem >= 201703
class StringFromPath {
public:
explicit StringFromPath(
const std::filesystem::path& path ABSL_ATTRIBUTE_LIFETIME_BOUND)
: path_(path) {}
/*implicit*/ operator std::string() const { return path_.string(); }
private:
const std::filesystem::path& path_;
};
#endif
PathInitializer() = default;
// Stores `str` converted to `absl::string_view` and then to `std::string`.
ABSL_ATTRIBUTE_ALWAYS_INLINE
/*implicit*/ PathInitializer(const char* str ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<MakerType<absl::string_view>>&&
storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: Initializer(std::move(storage).emplace(absl::string_view(str))) {}
// Stores `str` converted to `std::string`.
template <typename T,
std::enable_if_t<
std::conjunction_v<NotSameRef<PathInitializer, T>,
std::is_convertible<T&&, std::string>>,
int> = 0>
/*implicit*/ PathInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND)
: Initializer(std::forward<T>(str)) {}
#if __cpp_lib_filesystem >= 201703
// Stores `path.string()`.
/*implicit*/ PathInitializer(const std::filesystem::path& path
ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<StringFromPath>&& storage
ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: Initializer(std::move(storage).emplace(path)) {}
#endif
// Stores `str` converted to `PathRef`, then to `absl::string_view`, and then
// to `std::string`.
template <
typename T,
std::enable_if_t<std::conjunction_v<
NotSameRef<PathInitializer, T>,
std::negation<std::is_convertible<T&&, std::string>>,
#if __cpp_lib_filesystem >= 201703
NotSameRef<std::filesystem::path, T>,
#endif
std::is_convertible<T&&, StringRef>>,
int> = 0>
/*implicit*/ PathInitializer(T&& str ABSL_ATTRIBUTE_LIFETIME_BOUND,
TemporaryStorage<MakerType<absl::string_view>>&&
storage ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
: Initializer(
std::move(storage).emplace(StringRef(std::forward<T>(str)))) {
}
PathInitializer(PathInitializer&& that) = default;
PathInitializer& operator=(PathInitializer&&) = delete;
};
} // namespace riegeli
#endif // RIEGELI_BYTES_PATH_REF_H_