-
Notifications
You must be signed in to change notification settings - Fork 14
Expand file tree
/
Copy pathresolver.cppm
More file actions
102 lines (90 loc) · 3.5 KB
/
Copy pathresolver.cppm
File metadata and controls
102 lines (90 loc) · 3.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
// mcpp.pm.resolver — turn a SemVer constraint into a concrete version,
// using the package's xpkg lua descriptor as the version inventory.
//
// Part of the package-management subsystem refactor (PR-R4 in
// `.agents/docs/2026-05-08-pm-subsystem-architecture.md`). Strictly
// pulled out of `cli.cppm` with no behavior change; the same
// signatures, the same error strings, the same platform key picking.
export module mcpp.pm.resolver;
import std;
import mcpp.manifest;
import mcpp.pm.package_fetcher;
import mcpp.version_req;
export namespace mcpp::pm {
// xpkg.lua's `xpm.<key>` uses these names. (Distinct from
// `kCurrentPlatform` in cli.cppm, which is the [toolchain] table key —
// "macos" vs "macosx".)
inline constexpr std::string_view kXpkgPlatform =
#if defined(__linux__)
"linux";
#elif defined(__APPLE__)
"macosx";
#elif defined(_WIN32)
"windows";
#else
"linux";
#endif
// Returns true if `v` is a SemVer constraint (caret, tilde, range, glob,
// `*`, or empty) rather than a literal exact version. Empty counts as
// "constraint" so callers re-resolve via the index — bare `1.2.3` is
// treated as exact for back-compat with pre-SemVer pinning workflows;
// users opt into resolution by writing `^1.2.3` etc.
inline bool is_version_constraint(std::string_view v) {
if (v.empty()) return true;
if (v == "*") return true;
char c = v.front();
if (c == '^' || c == '~' || c == '>' || c == '<' || c == '=') return true;
if (v.find(',') != std::string_view::npos) return true;
return false;
}
// Resolve a SemVer constraint against the index entry's available
// versions. Returns the chosen exact version string, or an error
// message. The fetcher is used to read the lua descriptor for the
// requested package name.
inline std::expected<std::string, std::string>
resolve_semver(std::string_view name,
std::string_view constraint,
mcpp::pm::Fetcher& fetcher)
{
namespace vr = mcpp::version_req;
auto luaContent = fetcher.read_xpkg_lua(name);
if (!luaContent) {
return std::unexpected(std::format(
"dependency '{}' has SemVer constraint '{}' but the index entry "
"isn't cloned locally yet — run `mcpp index update` first",
name, constraint));
}
auto req = vr::parse_req(constraint);
if (!req) {
return std::unexpected(std::format(
"dependency '{}': invalid version constraint '{}': {}",
name, constraint, req.error()));
}
auto rawVersions = mcpp::manifest::list_xpkg_versions(*luaContent, kXpkgPlatform);
if (rawVersions.empty()) {
return std::unexpected(std::format(
"dependency '{}': index entry has no versions for platform '{}'",
name, kXpkgPlatform));
}
std::vector<vr::Version> parsed;
parsed.reserve(rawVersions.size());
for (auto& s : rawVersions) {
auto v = vr::parse_version(s);
if (!v) continue; // ignore unparseable entries
parsed.push_back(*v);
}
if (parsed.empty()) {
return std::unexpected(std::format(
"dependency '{}': no valid versions in index", name));
}
auto idx = vr::choose(*req, parsed);
if (!idx) {
std::string avail;
for (auto& s : rawVersions) { if (!avail.empty()) avail += ", "; avail += s; }
return std::unexpected(std::format(
"dependency '{}': constraint '{}' matches none of: [{}]",
name, constraint, avail));
}
return parsed[*idx].str();
}
} // namespace mcpp::pm