HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
path_location_intf.hpp
1// Copyright Take Vos 2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "cmake_install.hpp"
8#include "../utility/utility.hpp"
9#include "../metadata/metadata.hpp"
10#include "../macros.hpp"
11#include <filesystem>
12#include <ranges>
13#include <fstream>
14#include <string>
15#include <ranges>
16#include <expected>
17#include <system_error>
18
19hi_warning_push();
20// C4702 unreachable code: False positive, but "not a bug" / "low priority".
21// https://developercommunity.visualstudio.com/t/warning-c4702-for-range-based-for-loop/859129
22// The ranged for-loop is translated into a normal for-loop. The iterator
23// part of the normal for-loop is never executed due to the immediate return
24// inside the for-loop.
25hi_warning_ignore_msvc(4702);
26
31hi_export_module(hikogui.path.path_location : intf);
32
33hi_export namespace hi {
34inline namespace v1 {
35
36template<typename Context>
37concept path_range = std::ranges::input_range<Context> and
38 std::convertible_to<std::ranges::range_value_t<std::remove_cvref_t<Context>>, std::filesystem::path> and
39 not std::convertible_to<Context, std::filesystem::path>;
40
48template<path_range Locations>
49[[nodiscard]] inline generator<std::filesystem::path>
50find_path(Locations&& locations, std::filesystem::path const& ref) noexcept
51{
52 if (ref.is_absolute()) {
53 if (std::filesystem::exists(ref)) {
54 co_yield ref;
55 }
56
57 } else {
58 for (auto const& base : locations) {
59 auto const path = base / ref;
60 if (std::filesystem::exists(path)) {
61 co_yield path;
62 }
63 }
64 }
65}
66
74[[nodiscard]] inline generator<std::filesystem::path>
75find_path(std::filesystem::path const& location, std::filesystem::path const& ref) noexcept
76{
77 if (ref.is_absolute()) {
78 if (std::filesystem::exists(ref)) {
79 co_yield ref;
80 }
81
82 } else {
83 auto const path = location / ref;
84 if (std::filesystem::exists(path)) {
85 co_yield path;
86 }
87 }
88}
89
98template<path_range Locations>
99[[nodiscard]] inline std::filesystem::path get_path(Locations&& locations, std::filesystem::path const& ref)
100{
101 for (auto const& path : find_path(locations, ref)) {
102 return path;
103 }
104
105 throw io_error(std::format("Could not find '{}' in search-path: {}", ref.string(), to_string(locations)));
106}
107
116[[nodiscard]] inline std::filesystem::path get_path(std::filesystem::path const& location, std::filesystem::path const& ref)
117{
118 for (auto const& path : find_path(location, ref)) {
119 return path;
120 }
121
122 throw io_error(std::format("Could not find '{}' in: {}", ref.string(), location.string()));
123}
124
133[[nodiscard]] inline std::filesystem::path
134get_path(std::expected<std::filesystem::path, std::error_code> const& location, std::filesystem::path const& ref)
135{
136 if (not location) {
137 throw io_error(
138 std::format("Could not find '{}' because of an error at the location: {}", ref.string(), location.error().message()));
139 }
140
141 for (auto const& path : find_path(*location, ref)) {
142 return path;
143 }
144
145 throw io_error(std::format("Could not find '{}' in: {}", ref.string(), location->string()));
146}
147
153template<path_range Locations>
154[[nodiscard]] inline std::string to_string(Locations&& locations) noexcept
155{
156 auto r = std::string{};
157 for (auto const& path : locations) {
158 if (not r.empty()) {
159 r += ";";
160 }
161 r += path.string();
162 }
163 return r;
164}
165
169[[nodiscard]] std::expected<std::filesystem::path, std::error_code> executable_file() noexcept;
170
174[[nodiscard]] inline std::expected<std::filesystem::path, std::error_code> executable_dir() noexcept
175{
176 auto path = executable_file();
177 if (path) {
178 path->remove_filename();
179 }
180 return path;
181}
182
186[[nodiscard]] std::expected<std::filesystem::path, std::error_code> data_dir() noexcept;
187
191[[nodiscard]] std::expected<std::filesystem::path, std::error_code> log_dir() noexcept;
192
196[[nodiscard]] inline generator<std::filesystem::path> resource_dirs() noexcept;
197
201[[nodiscard]] inline generator<std::filesystem::path> system_font_dirs() noexcept;
202
206[[nodiscard]] inline generator<std::filesystem::path> font_files() noexcept;
207
211[[nodiscard]] inline generator<std::filesystem::path> theme_files() noexcept;
212
219[[nodiscard]] inline std::optional<std::filesystem::path> source_dir() noexcept
220{
221 static auto r = []() -> std::optional<std::filesystem::path> {
222 auto const executable_dir_ = executable_dir();
223 if (not executable_dir_) {
224 return std::nullopt;
225 }
226
227 // If the cmake_install.cmake file exists then the executable is located in a build directory.
228 if (auto tmp = parse_cmake_install(*executable_dir_ / "cmake_install.cmake")) {
229 return tmp->source_dir;
230 }
231
232 // When using a cmake multi-config generator The executable lives in the
233 // ./Debug/, ./Release/ or ./RelWithDebInfo/ directory.
234 // So, the cmake_install.cmake file is located one directory up.
235 if (auto tmp = parse_cmake_install(*executable_dir_ / ".." / "cmake_install.cmake")) {
236 return tmp->source_dir;
237 }
238
239 return std::nullopt;
240 }();
241
242 return r;
243}
244
245[[nodiscard]] inline std::filesystem::path library_source_dir() noexcept
246{
247 auto path = std::filesystem::path{__FILE__};
248 path.replace_filename("../../..");
249 return path.lexically_normal();
250}
251
252[[nodiscard]] inline std::filesystem::path library_test_data_dir() noexcept
253{
254 return hi::library_source_dir() / "tests" / "data";
255}
256
257} // namespace v1
258} // namespace hi::v1
259
260hi_warning_pop();
generator< std::filesystem::path > find_path(Locations &&locations, std::filesystem::path const &ref) noexcept
Find a path.
Definition path_location_intf.hpp:50
std::expected< std::filesystem::path, std::error_code > log_dir() noexcept
Get the full path to the directory where the application should store its log files.
Definition path_location_win32_impl.hpp:53
generator< std::filesystem::path > resource_dirs() noexcept
The directories to search for resource files.
Definition path_location_win32_impl.hpp:63
std::expected< std::filesystem::path, std::error_code > executable_dir() noexcept
Get the full path to the directory when this executable is located.
Definition path_location_intf.hpp:174
generator< std::filesystem::path > theme_files() noexcept
The directories to search for theme files of the application.
std::expected< std::filesystem::path, std::error_code > executable_file() noexcept
Get the full path to this executable.
Definition path_location_win32_impl.hpp:25
generator< std::filesystem::path > font_files() noexcept
The directories to search for font files of both the application and system.
generator< std::filesystem::path > system_font_dirs() noexcept
The directories to search for system font files.
Definition path_location_win32_impl.hpp:98
std::optional< std::filesystem::path > source_dir() noexcept
Get the full path to source code of this executable.
Definition path_location_intf.hpp:219
std::filesystem::path get_path(Locations &&locations, std::filesystem::path const &ref)
Get a path.
Definition path_location_intf.hpp:99
std::expected< std::filesystem::path, std::error_code > data_dir() noexcept
Get the full path to the directory where the application should store its data.
Definition path_location_win32_impl.hpp:38
STL namespace.
The HikoGUI namespace.
Definition array_generic.hpp:20
std::optional< cmake_install > parse_cmake_install(std::filesystem::path path) noexcept
Parse a cmake_install.cmake file.
Definition cmake_install.hpp:28
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Exception thrown during I/O on an error.
Definition exception_intf.hpp:173
Definition path_location_intf.hpp:37