// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® export module Crafter.Review:Refs; import std; import :Json; // The kernel modes' references: gate 0's prefetch of the lore messages an // artifact cites (a cache addressed by message id, shared across hashes), // the per-hash index, the digest the run layer carries, and a light persona // corpus (a lore search listing). lore.kernel.org is reached only through // `python3 utilities/lore.py` in the working directory, the one way past its // proof-of-work wall; a fetch never raises - a failure is a `failed` index // row the lenses are told about, never synthesized. export namespace Crafter::Review::Refs { // {reference: url}, in order of first appearance: the [n] lines of a // reply's reference list ("1", "2", ...), else every lore URL in the text // ("L1", "L2", ...). using Table = std::vector>; Table ParseRefs(std::string_view text); // The url reference n names, when the table is a numbered list. std::optional UrlOf(const Table& refs, std::int64_t n); // The message id a lore URL names: https?://lore.kernel.org/(r|all|)// std::optional MsgidOf(std::string_view url); // The file a message id is cached under: every character outside // [A-Za-z0-9._@-] an underscore, 200 characters at most. std::string SafeName(std::string_view msgid); // From, Date and Subject of a raw mail's header block, the first of each. std::map Headers(std::string_view raw); using Log = std::function; // (status, error) of one fetch: "fetched" with the bytes written to // `path`, or "failed" with the last error. The test seam: a fetcher set // here replaces the lore.py subprocess. using Fetcher = std::function(const std::filesystem::path& repo, const std::string& url, const std::filesystem::path& path)>; void SetFetcher(Fetcher fetcher); // `python3 utilities/lore.py fetch /raw` in `repo`, three tries. std::pair Fetch(const std::filesystem::path& repo, const std::string& url, const std::filesystem::path& path); // The index of every reference - {n: {url, msgid, path, status, error?, // From?, Date?, Subject?, bytes?}} - written to indexPath, the raw mails // under refsDir; `workers` fetches at once. Json Prefetch(const std::filesystem::path& repo, const Table& refs, const std::filesystem::path& refsDir, const std::filesystem::path& indexPath, const Log& log, std::int32_t workers = 4); // REFERENCES prefetched: one line per entry, numbered ones first. std::string Digest(const Json& index); // A lore search listing (subjects, dates, links) of the persona's own // mail since `since` (YYYYMMDD): the calibration map, cached under // refsDir as persona-.txt. (nullopt, "") when lore has none - an // empty corpus is never cached, so the next run retries. using Searcher = std::function(const std::filesystem::path& repo, const std::string& query)>; void SetSearcher(Searcher searcher); std::pair, std::string> PersonaCorpus(const std::filesystem::path& repo, std::string_view name, const std::filesystem::path& refsDir, std::string_view since, const Log& log); // YYYYMMDD of the day 365 days before an ISO date. std::string SinceOf(std::string_view isoDate); }