diff --git a/paths.hpp b/paths.hpp index 6bffeef4..716658e7 100644 --- a/paths.hpp +++ b/paths.hpp @@ -8,6 +8,8 @@ #pragma once +#include "concepts/named.hpp" + #include #include @@ -23,4 +25,30 @@ namespace cruft::paths { /// eg, "$TMPDIR/foo" and "%HOMEPATH%/foo" std::filesystem::path expand (std::filesystem::path const&); + + + /// Given a relative path, and a collection of candidate base paths, + /// return the first path that exists. + /// + /// Or, if none do, return the relative path itself (if it exists). + /// + /// Else throw a runtime_error. + /// + /// Note: there's a good chance if you're using this then it's likely + /// you'll run into TOCTOU errors. + template + std::filesystem::path + resolve_first (std::filesystem::path const &relative, ContainerT bases) + { + for (auto const &base: bases) { + auto const candidate = base / relative; + if (std::filesystem::exists (candidate)) + return candidate; + } + + if (exists (relative)) + return relative; + + throw std::runtime_error ("no path"); + } } \ No newline at end of file