module cargoxx.resolver; import std; import cargoxx.util; namespace cargoxx::resolver { namespace fs = std::filesystem; namespace { auto is_lib_filename(const fs::path& p) -> bool { auto name = p.filename().string(); if (!name.starts_with("lib")) { return false; } auto ext = p.extension().string(); if (ext == ".a") { return true; } if (ext == ".so" || ext == ".dylib") { return true; } // .so., .so.., ... — common shared-lib versioning. Use a // looser check: if the name contains ".so." or ".dylib." anywhere // after the lib prefix, accept it. return name.find(".so.") != std::string::npos || name.find(".dylib.") != std::string::npos; } } // namespace auto brute_scan(const fs::path& store_path, const std::string& package_name) -> util::Result { if (package_name.empty()) { return std::unexpected(util::Error{ util::ErrorCode::ResolutionUnknownPackage, "brute_scan: package name is empty", "", std::nullopt, std::nullopt, }); } const auto lib_dir = store_path / "lib"; const auto include_dir = store_path / "include"; BruteCandidate out; std::error_code ec; if (fs::exists(lib_dir, ec) && !ec) { for (const auto& entry : fs::directory_iterator{ lib_dir, fs::directory_options::skip_permission_denied, ec}) { if (!entry.is_regular_file() && !entry.is_symlink()) { continue; } if (!is_lib_filename(entry.path())) { continue; } out.lib_files.push_back(entry.path().string()); } std::ranges::sort(out.lib_files); } if (fs::exists(include_dir, ec) && !ec) { // For include/, expose the top-level directory itself (e.g. // `/include`) — that's what `#include ` // expects. Adding every subdir would also work, but is noisier // and provokes name collisions across deps. out.include_dirs.push_back(include_dir.string()); } if (out.lib_files.empty() && out.include_dirs.empty()) { return std::unexpected(util::Error{ util::ErrorCode::ResolutionUnknownPackage, std::format("no libs or headers under '{}'", store_path.string()), "", store_path, std::nullopt, }); } return out; } } // namespace cargoxx::resolver