@eagleoutice/flowr-dev
Version:
Static Dataflow Analyzer and Program Slicer for the R Programming Language
119 lines • 4.95 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.dayToMillis = dayToMillis;
exports.parseDateWindow = parseDateWindow;
exports.isDateBound = isDateBound;
exports.releaseDateBound = releaseDateBound;
exports.releasesOf = releasesOf;
exports.newestVersion = newestVersion;
exports.resolveVersion = resolveVersion;
/**
* Version/date resolution over a {@link PkgBlob}: parsing stored version strings into comparable
* {@link RVersion}s, decoding the day-granularity release dates, and picking the effective version for a query.
* Split out of `../sigdb` so the reader/writer there stays focused on the byte format.
*/
const r_version_1 = require("../../util/r-version");
/** milliseconds since the Unix epoch for a stored day count (dates are stored at day granularity to save space) */
function dayToMillis(day) {
return day * 86_400_000;
}
const DatePartsRegex = /^((?:19|20)\d{2})(?:\.(\d{1,2}))?(?:\.(\d{1,2}))?$/;
/** the comparators + `YYYY.MM.DD` date a release-date bound may use (a comparator disambiguates from a calendar version) */
const DateBoundRegex = /^(<=|>=|<|>|=)\s*((?:19|20)\d{2}(?:\.\d{1,2}){0,2})$/;
/**
* Parse a `YYYY`, `YYYY.MM`, or `YYYY.MM.DD` spec into its inclusive `[lower, upper]` millisecond window (the whole
* year / month / day named), or `undefined` when malformed or out of range (month `1..12`, day `1..31`).
*/
function parseDateWindow(spec) {
const m = DatePartsRegex.exec(spec.trim());
if (!m) {
return undefined;
}
const year = Number(m[1]);
const month = m[2] !== undefined ? Number(m[2]) : undefined;
const day = m[3] !== undefined ? Number(m[3]) : undefined;
// reject impossible month/day rather than letting Date.UTC silently roll over (`2021.13` -> Jan 2022)
if ((month !== undefined && (month < 1 || month > 12)) || (day !== undefined && (day < 1 || day > 31))) {
return undefined;
}
const lower = Date.UTC(year, (month ?? 1) - 1, day ?? 1);
const upper = day !== undefined ? lower + 86_400_000 - 1
: month !== undefined ? Date.UTC(year, month, 1) - 1
: Date.UTC(year + 1, 0, 1) - 1;
return { lower, upper };
}
/** whether `spec` is a release-date bound like `<=2026` or `>=2021.05` (a comparator is required) */
function isDateBound(spec) {
return DateBoundRegex.test(spec.trim());
}
/**
* A release-date predicate for a bound like `<=2026`, `>=2021.05.03` (`YYYY.MM.DD`, comparator required), or
* `undefined` when `spec` is not a date bound. An undated release (or an impossible date) never matches.
*/
function releaseDateBound(spec) {
const m = DateBoundRegex.exec(spec.trim());
if (!m) {
return undefined;
}
const cmp = m[1];
const window = parseDateWindow(m[2]);
if (!window) {
return () => false; // a valid comparator but an impossible date matches nothing
}
const { lower, upper } = window;
return date => {
if (date === undefined) {
return false;
}
const t = date.getTime();
switch (cmp) {
case '<=': return t <= upper;
case '<': return t < lower;
case '>=': return t >= lower;
case '>': return t > upper;
default: return t >= lower && t <= upper;
}
};
}
/**
* The known releases of a blob, ascending by R-version order (empty when no dates were stored). Versions that
* differ in writing but not in order (`1.2` and `1.2.0`) compare equal, so their dates settle them.
*/
function releasesOf(blob) {
if (!blob) {
return [];
}
return Object.entries(blob.dates)
.map(([ver, day]) => ({ version: r_version_1.RVersion.parseOrZero(ver), date: new Date(dayToMillis(day)) }))
.sort((a, b) => r_version_1.RVersion.compare(a.version.str, b.version.str) || (a.date.getTime() - b.date.getTime()));
}
/** the version with the newest release date (falling back to `latest`, then the highest by R-version order) */
function newestVersion(blob, latest) {
let best;
let bestDay = -Infinity;
for (const [ver, day] of Object.entries(blob.dates)) {
if (day > bestDay) {
bestDay = day;
best = ver;
}
}
if (best !== undefined) {
return best;
}
if (blob.versions[latest]) {
return latest;
}
// no dates and no recorded latest: pick the highest by R's numeric-version order (not string order)
return r_version_1.RVersion.highest(Object.keys(blob.versions));
}
/** pick the version tuple: requested, else the package's latest, else the highest present by R-version order */
function resolveVersion(blob, latest, version) {
if (version && blob.versions[version]) {
return version;
}
if (blob.versions[latest]) {
return latest;
}
return r_version_1.RVersion.highest(Object.keys(blob.versions));
}
//# sourceMappingURL=sigdb-version.js.map