https://mooseframework.inl.gov
DataFileUtils.C
Go to the documentation of this file.
1 //* This file is part of the MOOSE framework
2 //* https://mooseframework.inl.gov
3 //*
4 //* All rights reserved, see COPYRIGHT for full restrictions
5 //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
6 //*
7 //* Licensed under LGPL 2.1, please see LICENSE for details
8 //* https://www.gnu.org/licenses/lgpl-2.1.html
9 
10 #include "DataFileUtils.h"
11 #include "Moose.h"
12 #include "MooseError.h"
13 #include "MooseUtils.h"
14 #include "Registry.h"
15 
16 #include <filesystem>
17 #include <regex>
18 
19 namespace Moose::DataFileUtils
20 {
22 getPath(std::string path, const GetPathOptions & options)
23 {
24  const auto & data_paths = Registry::getRegistry().getDataFilePaths();
25 
26  // Search for "<name>:" prefix which is an explicit data name to search
27  std::optional<std::pair<std::string, std::string>> explicit_data;
28  if (std::smatch match; std::regex_search(path, match, std::regex("(?:([a-z0-9_]+):)?(.*)")))
29  {
30  // Has an explicit data name
31  if (match[1].matched)
32  {
33  if (const auto it = data_paths.find(match[1].str()); it != data_paths.end())
34  explicit_data = *it;
35  else
36  mooseError("Data from '", match[1], "' is not registered to be searched");
37  }
38  path = match[2];
39  }
40  else
41  mooseError("Failed to parse path '", path, "'");
42 
43  const std::filesystem::path value_path = std::filesystem::path(path);
44 
45  // Helper for erroring if the data name is explicitly set
46  const auto if_data_name_error = [&explicit_data](const auto & message)
47  {
48  if (explicit_data)
49  mooseError("Cannot use ",
50  message,
51  " along with a data name to search (requested "
52  "to search in '",
53  explicit_data->first,
54  "')");
55  };
56 
57  // File is absolute, no need to search
58  if (std::filesystem::path(path).is_absolute())
59  {
60  // Shouldn't provide an absolute path and an explicit data name
61  if_data_name_error("an absolute path");
62 
63  // File exists, we're good to go
64  if (MooseUtils::checkFileReadable(path, false, false, false))
66 
67  // If absolute and graceful, return the bad absolute path
68  if (options.graceful)
70 
71  // If absolute and not graceful, nothing else to do
72  mooseError("The absolute path '", path, "' does not exist or is not readable.");
73  }
74 
75  // Keep track of what was was searched for error context
76  std::map<std::string, std::string> not_found;
77 
78  // Because no explicit data name is set, we always want to prefer
79  // checking relative paths first before searching the data
80  if (!explicit_data)
81  {
82  const std::string base = options.base ? *options.base : std::filesystem::current_path().c_str();
83  const auto relative_to_base = MooseUtils::pathjoin(base, path);
84 
85  // Relative path exists
86  if (MooseUtils::checkFileReadable(relative_to_base, false, false, false))
87  return {MooseUtils::canonicalPath(relative_to_base), Context::RELATIVE};
88 
89  // Without an explicit data name set, if we're set to not search all data
90  // there's nothing else to check here so we'll avoid the error at the bottom
91  // and exit immediately
92  if (!options.search_all_data && options.graceful)
93  return {MooseUtils::canonicalPath(relative_to_base), Context::RELATIVE_NOT_FOUND};
94 
95  // Mark that we searched the working directory
96  not_found.emplace("working directory", MooseUtils::canonicalPath(base));
97  }
98 
99  // See if we should skip searching data
100  std::optional<std::string> skip_data_reason;
101  // Path starts with ./ so don't search data
102  if (path.size() > 1 && path.substr(0, 2) == "./")
103  {
104  // Shouldn't provide a relative path and an explicit data name
105  if_data_name_error("a path that starts with './'");
106 
107  // Mark that we're not checking data because it is a relative path
108  skip_data_reason = "begins with './'";
109  }
110  // Path resolves outside of . so don't search data
111  else if (const std::string proximate = std::filesystem::proximate(path).c_str();
112  (proximate.size() > 1 && proximate.substr(0, 2) == ".."))
113  {
114  // Shouldn't provide a relative path and an explicit data name
115  if_data_name_error("a relative path");
116 
117  // Mark that we're not checking data because it's out of the cwd
118  skip_data_reason = "resolves behind '.'";
119  }
120 
121  // Search the data if we found a reason not to and it's set
122  // to search data or we have an explicit data name to search
123  std::map<std::string, std::string> found;
124  if (!skip_data_reason && (options.search_all_data || explicit_data))
125  {
126  const auto check_data_path = [&found, &not_found, &path](const auto & entry)
127  {
128  const auto & [name, data_path] = entry;
129  const auto file_path = MooseUtils::pathjoin(data_path, path);
130  if (MooseUtils::checkFileReadable(file_path, false, false, false))
131  found.emplace(name, MooseUtils::canonicalPath(file_path));
132  else
133  not_found.emplace(name + " data", data_path);
134  };
135 
136  // Explicit search, just searching one
137  if (explicit_data)
138  {
139  check_data_path(*explicit_data);
140  if (found.empty())
141  mooseError("The path '", path, "' was not found in data from '", explicit_data->first, "'");
142  }
143  // Search all data paths
144  else
145  {
146  for (const auto & entry : data_paths)
147  check_data_path(entry);
148  }
149  }
150 
151  // Found exactly one
152  if (found.size() == 1)
153  {
154  const auto & [name, data_path] = *found.begin();
155  return {MooseUtils::canonicalPath(data_path), Context::DATA, name};
156  }
157 
158  std::stringstream oss;
159  // Found multiple
160  if (found.size() > 1)
161  {
162  oss << "Multiple files were found when searching for the data file '" << path << "':\n\n";
163  for (const auto & [name, data_path] : found)
164  oss << " " << name << ": " << data_path << "\n";
165  const auto & first_name = found.begin()->first;
166  oss << "\nYou can resolve this ambiguity by appending a prefix with the desired data name, for "
167  "example:\n\n "
168  << first_name << ":" << path;
169  }
170  // Found none
171  else
172  {
173  oss << "Unable to find the data file '" << path << "'.\n";
174  if (not_found.size())
175  {
176  oss << "\nPaths searched:\n";
177  for (const auto & [name, data_path] : not_found)
178  oss << " " << name << ": " << data_path << "\n";
179  }
180  if (skip_data_reason)
181  oss << "\nData path(s) were not searched because search path " << *skip_data_reason << ".\n";
182  }
183 
184  mooseError(oss.str());
185 }
186 
188 getPathExplicit(const std::string & data_name,
189  const std::string & path,
190  const std::optional<std::string> & base)
191 {
192  GetPathOptions options;
193  options.base = base;
194  return getPath(data_name + ":" + path, options);
195 }
196 }
std::string name(const ElemQuality q)
Path getPathExplicit(const std::string &data_name, const std::string &path, const std::optional< std::string > &base=std::optional< std::string >())
Get the data path for a given path, searching the registered data given an explicit data search path...
Relative to the base (typically an input file)
static Registry & getRegistry()
Get the global Registry singleton.
Definition: Registry.C:24
bool search_all_data
Whether or not to search all registered data.
Definition: DataFileUtils.h:67
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:311
From installed/in-tree data.
bool graceful
Whether or not to error whenever a path is not found.
Definition: DataFileUtils.h:72
Relative to the base, but not found.
Path getPath(std::string path, const GetPathOptions &options={})
Get the data path for a given path, searching the registered data.
Definition: DataFileUtils.C:22
std::string canonicalPath(const std::string &path)
Definition: MooseUtils.C:1173
Options to be passed to getPath().
Definition: DataFileUtils.h:60
Representation of a data file path.
Definition: DataFileUtils.h:40
std::optional< std::string > base
The base path by which to search for relative paths.
Definition: DataFileUtils.h:65
std::filesystem::path pathjoin(const std::filesystem::path &p)
Definition: MooseUtils.C:59
static const std::map< std::string, std::string > & getDataFilePaths()
Returns a map of all registered data file paths (name -> path)
Definition: Registry.h:258
bool checkFileReadable(const std::string &filename, bool check_line_endings, bool throw_on_unreadable, bool check_for_git_lfs_pointer)
Definition: MooseUtils.C:254