XGCRPWKLJYWGVCMHDIR2K3PVT2R22JALVV5K2IXTHU3JBAEYANGAC #ifndef NIX_PLUGIN_PIJUL_REPO_H#define NIX_PLUGIN_PIJUL_REPO_H#include <optional>#include <date/date.h>#include <types.hh>namespace nixpluginpijul{struct RepoStatus {std::string channel;std::string state;uint64_t lastModified;};std::string runPijul(nix::Strings args, std::optional<nix::Path> chdir = {}, std::optional<std::string> input = {}, bool isInteractive = false);RepoStatus getRepoStatus(const nix::PathView &repoPath);std::pair<std::string, uint64_t> getState(const nix::PathView &repoPath);std::string getRepoChannel(const nix::PathView &repoPath);date::sys_time<std::chrono::milliseconds> parseRFC3339(const std::string &spec);}#endif // NIX_PLUGIN_PIJUL_REPO_H
#include "repo.h"#include <types.hh>#include <util.hh>#if NIX_VERSION >= 0x021900#include <processes.hh>#endif#include <nlohmann/json.hpp>using nlohmann::json;using namespace nix;using namespace std::string_literals;using namespace std::string_view_literals;namespace nixpluginpijul{std::string runPijul(Strings args, std::optional<Path> chdir, std::optional<std::string> input, bool isInteractive){auto program = "pijul"sv;auto res = runProgram(RunOptions{.program = std::string(program),.searchPath = true,.args = std::move(args),.chdir = std::move(chdir),.input = std::move(input),.isInteractive = isInteractive,});if (!statusOk(res.first)) {throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));}return res.second;}RepoStatus getRepoStatus(const PathView &repoPath){auto [state, lastModified] = getState(repoPath);auto channel = getRepoChannel(repoPath);return RepoStatus{.channel = std::move(channel),.state = std::move(state),.lastModified = lastModified,};}std::pair<std::string, uint64_t> getState(const PathView &repoPath){const auto &output = runPijul({"log", "--output-format", "json", "--state", "--limit", "1"}, Path(repoPath));const auto &json = json::parse(output);const auto &commitInfo = json.at(0);const auto ×tampSpec = commitInfo.at("timestamp").get<std::string>();const uint64_t timestamp = std::chrono::duration_cast<std::chrono::seconds>(parseRFC3339(timestampSpec).time_since_epoch()).count();const std::string &state = commitInfo.at("state");return {state, timestamp};}std::string getRepoChannel(const PathView &repoPath){const auto &output = runPijul({"channel"}, Path(repoPath));std::string::size_type pos = 0;do {const auto nl = output.find('\n', pos);const auto line = std::string_view(output).substr(pos, nl - pos);if (line.empty()) {continue;}if (line.at(0) == '*') {return std::string(line.substr(2));}pos = nl;} while (pos != std::string::npos);throw Error("could not parse current channel"s);}date::sys_time<std::chrono::milliseconds> parseRFC3339(const std::string &spec){std::istringstream in{spec};date::sys_time<std::chrono::milliseconds> pt;in >> date::parse("%FT%TZ", pt);if (in.fail()) {in.clear();in.exceptions(std::ios::failbit);in.str(spec);in >> date::parse("%FT%T%Ez", pt);}return pt;}}
std::string runPijul(Strings args, std::optional<Path> chdir = {}, std::optional<std::string> input = {}, bool isInteractive = false){auto program = "pijul"sv;auto res = runProgram(RunOptions{.program = std::string(program),.searchPath = true,.args = std::move(args),.chdir = std::move(chdir),.input = std::move(input),.isInteractive = isInteractive,});if (!statusOk(res.first)) {throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));}return res.second;}
static RepoStatus getRepoStatus(const PathView &repoPath){auto [state, lastModified] = getState(repoPath);auto channel = getRepoChannel(repoPath);return RepoStatus{.channel = std::move(channel),.state = std::move(state),.lastModified = lastModified,};}static std::pair<std::string, uint64_t> getState(const PathView &repoPath){const auto &output = runPijul({"log", "--output-format", "json", "--state", "--limit", "1"}, Path(repoPath));const auto &json = nlohmann::json::parse(output);const auto &commitInfo = json.at(0);const auto ×tampSpec = commitInfo.at("timestamp").get<std::string>();const uint64_t timestamp = std::chrono::duration_cast<std::chrono::seconds>(parseRFC3339(timestampSpec).time_since_epoch()).count();const std::string &state = commitInfo.at("state");return {state, timestamp};}static date::sys_seconds parseRFC3339(const std::string &spec){std::istringstream in;date::sys_seconds pt;in >> date::parse("%FT%TZ", pt);if (in.fail()) {in.clear();in.exceptions(std::ios::failbit);in >> date::parse("%FT%T%Ez", pt);}return pt;}static std::string getRepoChannel(const PathView &repoPath){const auto &output = runPijul({"channel"}, Path(repoPath));std::string::size_type pos = 0;do {const auto nl = output.find('\n', pos);const auto line = std::string_view(output).substr(pos, nl - pos);if (line.empty()) {continue;}if (line.at(0) == '*') {return std::string(line.substr(2));}pos = nl;} while (pos != std::string::npos);throw Error("could not parse current channel"s);}