LCOV - code coverage report
Current view: top level - src/utils - DataFileUtils.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 62 63 98.4 %
Date: 2025-07-17 01:28:37 Functions: 2 2 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : {
      21             : Moose::DataFileUtils::Path
      22         265 : getPath(std::string path, const std::optional<std::string> & base)
      23             : {
      24         265 :   const auto & data_paths = Registry::getRegistry().getDataFilePaths();
      25             : 
      26             :   // Search for "<name>:" prefix which is a data name to limit the search to
      27         265 :   std::optional<std::string> data_name;
      28         265 :   std::smatch match;
      29         265 :   if (std::regex_search(path, match, std::regex("(?:(\\w+):)?(.*)")))
      30             :   {
      31         265 :     if (match[1].matched)
      32             :     {
      33           6 :       data_name = match[1];
      34           6 :       if (!data_paths.count(*data_name))
      35           1 :         mooseError("Data from '", *data_name, "' is not registered to be searched");
      36             :     }
      37         264 :     path = match[2];
      38             :   }
      39             :   else
      40           0 :     mooseError("Failed to parse path '", path, "'");
      41             : 
      42         264 :   const std::filesystem::path value_path = std::filesystem::path(path);
      43             : 
      44             :   // File is absolute, no need to search
      45         264 :   if (std::filesystem::path(path).is_absolute())
      46             :   {
      47          26 :     if (data_name)
      48           1 :       mooseError("Should not specify an absolute path along with a data name to search (requested "
      49             :                  "to search in '",
      50             :                  *data_name,
      51             :                  "')");
      52          25 :     if (MooseUtils::checkFileReadable(path, false, false, false))
      53          24 :       return {MooseUtils::canonicalPath(path), Context::ABSOLUTE};
      54           1 :     mooseError("The absolute path '", path, "' does not exist or is not readable.");
      55             :   }
      56             : 
      57             :   // Keep track of what was was searched for error context
      58         238 :   std::map<std::string, std::string> not_found;
      59             : 
      60             :   // Relative to the base, if provided
      61         238 :   if (base)
      62             :   {
      63         186 :     const auto relative_to_base = MooseUtils::pathjoin(*base, path);
      64         186 :     if (MooseUtils::checkFileReadable(relative_to_base, false, false, false))
      65         151 :       return {MooseUtils::canonicalPath(relative_to_base), Context::RELATIVE};
      66          35 :     not_found.emplace("working directory", MooseUtils::canonicalPath(*base));
      67         186 :   }
      68             : 
      69             :   // See if we should skip searching data
      70          87 :   std::optional<std::string> skip_data_reason;
      71             :   // Path starts with ./ so don't search data
      72          87 :   if (path.size() > 1 && path.substr(0, 2) == "./")
      73             :   {
      74           1 :     skip_data_reason = "begins with './'";
      75             :   }
      76             :   else
      77             :   {
      78             :     // Path resolves outside of . so don't search data
      79          86 :     const std::string proximate = std::filesystem::proximate(path).c_str();
      80          86 :     if (proximate.size() > 1 && proximate.substr(0, 2) == "..")
      81             :     {
      82           1 :       skip_data_reason = "resolves behind '.'";
      83             :     }
      84          86 :   }
      85             : 
      86             :   // Search data if we don't have a reason not to
      87          87 :   std::map<std::string, std::string> found;
      88          87 :   if (!skip_data_reason)
      89         179 :     for (const auto & [name, data_path] : data_paths)
      90             :     {
      91             :       // Explicit search, name doesn't match requested name
      92          94 :       if (data_name && name != *data_name) // explicit search
      93           4 :         continue;
      94          90 :       const auto file_path = MooseUtils::pathjoin(data_path, path);
      95          90 :       if (MooseUtils::checkFileReadable(file_path, false, false, false))
      96          76 :         found.emplace(name, MooseUtils::canonicalPath(file_path));
      97             :       else
      98          14 :         not_found.emplace(name + " data", data_path);
      99          90 :     }
     100             : 
     101             :   // Found exactly one
     102          87 :   if (found.size() == 1)
     103             :   {
     104          74 :     const auto & [name, data_path] = *found.begin();
     105          74 :     return {MooseUtils::canonicalPath(data_path), Context::DATA, name};
     106             :   }
     107             : 
     108          13 :   std::stringstream oss;
     109             :   // Found multiple
     110          13 :   if (found.size() > 1)
     111             :   {
     112           1 :     oss << "Multiple files were found when searching for the data file '" << path << "':\n\n";
     113           3 :     for (const auto & [name, data_path] : found)
     114           2 :       oss << "  " << name << ": " << data_path << "\n";
     115           1 :     const auto & first_name = found.begin()->first;
     116             :     oss << "\nYou can resolve this ambiguity by appending a prefix with the desired data name, for "
     117             :            "example:\n\n  "
     118           1 :         << first_name << ":" << path;
     119             :   }
     120             :   // Found none
     121             :   else
     122             :   {
     123          12 :     oss << "Unable to find the data file '" << path << "' anywhere.\n";
     124          12 :     if (not_found.size())
     125             :     {
     126          10 :       oss << "\nPaths searched:\n";
     127          27 :       for (const auto & [name, data_path] : not_found)
     128          17 :         oss << "  " << name << ": " << data_path << "\n";
     129             :     }
     130          12 :     if (skip_data_reason)
     131           2 :       oss << "\nData path(s) were not searched because search path " << *skip_data_reason << ".\n";
     132             :   }
     133             : 
     134          26 :   mooseError(oss.str());
     135         348 : }
     136             : 
     137             : Moose::DataFileUtils::Path
     138           2 : getPathExplicit(const std::string & data_name,
     139             :                 const std::string & path,
     140             :                 const std::optional<std::string> & base)
     141             : {
     142           4 :   return getPath(data_name + ":" + path, base);
     143             : }
     144             : }

Generated by: LCOV version 1.14