dasmig-core 1.2.0
Shared random engine, distributions and data tiers for the dasmig generator libraries
Loading...
Searching...
No Matches
resources.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: MIT
2// Copyright (c) 2020-2026 Diego Dasso Migotto
3
4#ifndef DASMIG_CORE_RESOURCES_HPP
5#define DASMIG_CORE_RESOURCES_HPP
6
7#include "dataset.hpp"
8#include "tsv.hpp"
9
10#include <cstddef>
11#include <filesystem>
12#include <optional>
13#include <span>
14#include <string>
15#include <string_view>
16#include <system_error>
17#include <utility>
18#include <vector>
19
20/// @file resources.hpp
21/// @brief Finding a library's data: compiled-in files, or a `resources/`
22/// directory found on disk.
23/// @author Diego Dasso Migotto (diegomigotto at hotmail dot com)
24
25namespace dasmig::core
26{
27
28/// @brief The directory name of a data tier.
29/// @param tier The tier.
30/// @return `"lite"` or `"full"`.
31[[nodiscard]] constexpr std::string_view tier_name(dataset tier) noexcept
32{
33 return tier == dataset::full ? "full" : "lite";
34}
35
36/// @brief A data file compiled into the program.
37///
38/// Generated headers (`scripts/embed.py`) define arrays of these, so a build
39/// without a filesystem (WebAssembly) still has its data. The content is
40/// split into chunks because compilers limit the size of one string literal
41/// (MSVC: about 64 KB).
43{
44 std::string_view path; ///< Path below `resources/`, e.g. `lite/biodata.tsv`.
45 std::span<const std::string_view> chunks; ///< The content, in order.
46
47 /// @brief The whole content.
48 /// @return The chunks joined.
49 [[nodiscard]] std::string text() const
50 {
51 std::size_t size = 0;
52 for (const auto chunk : chunks) size += chunk.size();
53 std::string out;
54 out.reserve(size);
55 for (const auto chunk : chunks) out += chunk;
56 return out;
57 }
58};
59
60/// @brief Find a compiled-in file by path.
61/// @param files The embedded files (empty when none are compiled in).
62/// @param path Path below `resources/`, with `/` separators.
63/// @return The file, or nullptr.
64[[nodiscard]] constexpr const embedded_file* find_embedded(std::span<const embedded_file> files,
65 std::string_view path) noexcept
66{
67 for (const auto& file : files)
68 {
69 if (file.path == path) return &file;
70 }
71 return nullptr;
72}
73
74/// @brief The directories searched for a library's `resources/`, in order.
75///
76/// Relative to @p base: `resources`, `../resources`, `<library>/resources`
77/// and `../<library>/resources`. These cover running from the repository,
78/// from its build directory, and from a parent directory holding several
79/// libraries side by side.
80///
81/// @param library Repository name, e.g. `biodata-generator`.
82/// @param base Directory to search from (default: the working directory).
83/// @return The candidate directories.
84[[nodiscard]] inline std::vector<std::filesystem::path> resource_candidates(
85 std::string_view library, const std::filesystem::path& base = std::filesystem::current_path())
86{
87 const std::filesystem::path lib{library};
88 return {base / "resources", base / ".." / "resources", base / lib / "resources",
89 base / ".." / lib / "resources"};
90}
91
92/// @brief The first candidate directory that contains a marker file.
93///
94/// The marker tells a library's `resources/` apart from another library's
95/// (for example `lite/biodata.tsv`).
96///
97/// @param library Repository name, e.g. `biodata-generator`.
98/// @param marker A file path below `resources/` that the library ships.
99/// @param base Directory to search from (default: the working directory).
100/// @return The `resources/` directory, or empty when none is found.
101// A swapped library and marker fails on the first lookup (a marker is a path
102// such as lite/x.tsv), so the adjacent string parameters are kept.
103[[nodiscard]] inline std::optional<std::filesystem::path> find_resources(
104 std::string_view library, std::string_view marker, // NOLINT(bugprone-easily-swappable-parameters)
105 const std::filesystem::path& base = std::filesystem::current_path())
106{
107 for (const auto& dir : resource_candidates(library, base))
108 {
109 std::error_code ec;
110 if (std::filesystem::is_regular_file(dir / marker, ec)) return dir;
111 }
112 return std::nullopt;
113}
114
115/// @brief Load a data file, compiled-in if available, otherwise from disk.
116///
117/// Embedded files win, so a build with compiled-in data never depends on the
118/// working directory. The two give the same tsv_document::content_hash(),
119/// so seeds replay the same either way.
120///
121/// @param path Path below `resources/`, e.g. `lite/biodata.tsv`.
122/// @param embedded Compiled-in files (empty when none).
123/// @param root A `resources/` directory (from find_resources()), or empty.
124/// @param columns Column names for a headerless file; empty when the first
125/// line names them.
126/// @return The document, or empty when the file is neither embedded nor
127/// present under @p root.
128/// @throws tsv_error If the file exists but cannot be parsed.
129[[nodiscard]] inline std::optional<tsv_document> load_resource(
130 std::string_view path, std::span<const embedded_file> embedded,
131 const std::optional<std::filesystem::path>& root, std::vector<std::string> columns = {})
132{
133 if (const auto* file = find_embedded(embedded, path))
134 {
135 std::string source = "embedded:" + std::string{path};
136 if (columns.empty()) return tsv_document::parse(file->text(), std::move(source));
137 return tsv_document::parse(file->text(), std::move(source), std::move(columns));
138 }
139 if (!root) return std::nullopt;
140 const auto file = *root / path;
141 std::error_code ec;
142 if (!std::filesystem::is_regular_file(file, ec)) return std::nullopt;
143 return tsv_document::load(file, std::move(columns));
144}
145
146} // namespace dasmig::core
147
148#endif // DASMIG_CORE_RESOURCES_HPP
static tsv_document load(const std::filesystem::path &path, std::vector< std::string > columns={})
Read and parse a file.
Definition tsv.hpp:460
static tsv_document parse(std::string_view text, std::string source)
Parse text whose first line names the columns.
Definition tsv.hpp:433
The data tier enum shared by every generator library.
dataset
Which tier of a library's bundled data to load.
Definition dataset.hpp:23
std::optional< tsv_document > load_resource(std::string_view path, std::span< const embedded_file > embedded, const std::optional< std::filesystem::path > &root, std::vector< std::string > columns={})
Load a data file, compiled-in if available, otherwise from disk.
constexpr const embedded_file * find_embedded(std::span< const embedded_file > files, std::string_view path) noexcept
Find a compiled-in file by path.
Definition resources.hpp:64
constexpr std::string_view tier_name(dataset tier) noexcept
The directory name of a data tier.
Definition resources.hpp:31
std::vector< std::filesystem::path > resource_candidates(std::string_view library, const std::filesystem::path &base=std::filesystem::current_path())
The directories searched for a library's resources/, in order.
Definition resources.hpp:84
std::optional< std::filesystem::path > find_resources(std::string_view library, std::string_view marker, const std::filesystem::path &base=std::filesystem::current_path())
The first candidate directory that contains a marker file.
A data file compiled into the program.
Definition resources.hpp:43
std::string_view path
Path below resources/, e.g. lite/biodata.tsv.
Definition resources.hpp:44
std::string text() const
The whole content.
Definition resources.hpp:49
std::span< const std::string_view > chunks
The content, in order.
Definition resources.hpp:45
Tab-separated data files with typed access and located errors.