1#include "nix-eval/src/lib.rs"2#include "lib.hh"3#include <nix/fetchers/fetch-settings.hh>4#include <nix/store/build-result.hh>5#include <nix/store/content-address.hh>6#include <nix/store/derived-path.hh>7#include <nix/store/local-fs-store.hh>8#include <nix/store/outputs-spec.hh>9#include <nix/store/path-info.hh>10#include <nix/store/profiles.hh>11#include <nix/store/realisation.hh>12#include <nix/store/store-api.hh>13#include <nix/util/file-system.hh>14#include <nix/util/hash.hh>15#include <nix/util/posix-source-accessor.hh>16#include <nix/util/ref.hh>17#include <nix/util/serialise.hh>18#include <nix/util/signature/local-keys.hh>19#include <nix/util/signature/signer.hh>20#include <nix/util/source-path.hh>21#include <nix_api_fetchers.h>22#include <nix_api_store_internal.h>23#include <sstream>2425struct nix_fetchers_settings {26 nix::ref<nix::fetchers::Settings> settings;27};2829extern "C" {30void set_fetcher_setting(nix_fetchers_settings *settings_struct,31 const char *setting, const char *value) {32 auto &settings_ref = settings_struct->settings;33 bool result = settings_ref->set(setting, value);34}35}3637rust::String switch_profile(Store *store, rust::Str profile,38 rust::Str store_path) {39 try {40 auto nixStore = store->ptr;41 auto *lfs = dynamic_cast<nix::LocalFSStore *>(&*nixStore);42 if (!lfs)43 return rust::String("destination is not a local-fs store");44 auto path = nixStore->parseStorePath(std::string(store_path));45 std::filesystem::path prof = std::string(profile);46 auto gen = nix::createGeneration(*lfs, prof, path);47 nix::switchLink(prof, gen);48 return rust::String();49 } catch (const std::exception &e) {50 return rust::String(e.what());51 }52}5354rust::String sign_closure(Store *store, rust::Str store_path,55 rust::Str key_file) {56 try {57 auto nixStore = store->ptr;58 nix::LocalSigner signer(59 nix::SecretKey::parse(nix::readFile(std::string(key_file))));60 auto root = nixStore->parseStorePath(std::string(store_path));61 nix::StorePathSet closure;62 nixStore->computeFSClosure(root, closure);63 for (auto &p : closure) {64 auto info = nixStore->queryPathInfo(p);65 nix::ValidPathInfo info2(*info);66 info2.sign(*nixStore, signer);67 nixStore->addSignatures(p, info2.sigs);68 }69 return rust::String();70 } catch (const std::exception &e) {71 return rust::String(e.what());72 }73}7475CxxListGenerationsResult list_generations(rust::Str profile_path) {76 CxxListGenerationsResult out{rust::String(), {}};77 try {78 auto [gens, current] =79 nix::findGenerations(std::filesystem::path(std::string(profile_path)));80 for (auto &g : gens) {81 CxxProfileGeneration cg{};82 cg.id = g.number;83 cg.store_path = rust::String(g.path.string());84 cg.creation_time_unix = static_cast<int64_t>(g.creationTime);85 cg.current = current.has_value() && *current == g.number;86 out.generations.push_back(cg);87 }88 } catch (const std::exception &e) {89 out.error = rust::String(e.what());90 }91 return out;92}9394static std::vector<std::string> split_lines(rust::Str joined) {95 std::vector<std::string> out;96 std::string buf;97 std::istringstream iss((std::string(joined)));98 while (std::getline(iss, buf)) {99 if (!buf.empty())100 out.push_back(buf);101 }102 return out;103}104105CxxBuildResult build_drv_outputs(Store *store, rust::Str drv_path,106 // TODO: Vec<str>107 rust::Str output_names_joined) {108 CxxBuildResult res{rust::String(), {}};109 try {110 auto nixStore = store->ptr;111 auto sp = nixStore->parseStorePath(std::string(drv_path));112113 nix::DerivedPath::Built built{114 .drvPath = nix::makeConstantStorePathRef(sp),115 .outputs = nix::OutputsSpec{nix::OutputsSpec::All{}},116 };117 auto names = split_lines(output_names_joined);118 if (!names.empty()) {119 std::set<nix::OutputName, std::less<>> nameSet;120 for (auto &n : names)121 nameSet.insert(n);122 built.outputs =123 nix::OutputsSpec{nix::OutputsSpec::Names{std::move(nameSet)}};124 }125126 std::vector<nix::DerivedPath> reqs;127 reqs.push_back(built);128129 auto results = nixStore->buildPathsWithResults(reqs);130 for (auto &r : results) {131 if (auto *failure = r.tryGetFailure()) {132 res.error = rust::String(failure->what());133 return res;134 }135 }136 for (auto &r : results) {137 if (auto *success = r.tryGetSuccess()) {138 for (auto &[name, real] : success->builtOutputs) {139 res.outputs.push_back(140 rust::String(nixStore->printStorePath(real.outPath)));141 }142 }143 }144 } catch (const std::exception &e) {145 res.error = rust::String(e.what());146 }147 return res;148}149150CxxBuildResult substitute_paths(Store *store, rust::Str paths_joined) {151 CxxBuildResult res{rust::String(), {}};152 try {153 auto nixStore = store->ptr;154 auto paths = split_lines(paths_joined);155156 std::vector<nix::DerivedPath> reqs;157 reqs.reserve(paths.size());158 for (auto &p : paths) {159 reqs.push_back(nix::DerivedPath::Opaque{nixStore->parseStorePath(p)});160 }161162 // Substituter miss will cause scheduler to trigger build163 auto results = nixStore->buildPathsWithResults(reqs);164 for (auto &r : results) {165 if (r.tryGetSuccess() == nullptr)166 continue;167 if (auto *opaque = std::get_if<nix::DerivedPath::Opaque>(&r.path.raw())) {168 res.outputs.push_back(169 rust::String(nixStore->printStorePath(opaque->path)));170 }171 }172 } catch (const std::exception &e) {173 res.error = rust::String(e.what());174 }175 return res;176}177178bool is_valid_path(Store *store, rust::Str path) {179 try {180 auto nixStore = store->ptr;181 auto sp = nixStore->parseStorePath(std::string(path));182 return nixStore->isValidPath(sp);183 } catch (const std::exception &) {184 return false;185 }186}187188CxxPathInfo query_path_info(Store *store, rust::Str path) {189 CxxPathInfo out{rust::String(), rust::String(), 0, {}, {}};190 try {191 auto nixStore = store->ptr;192 auto sp = nixStore->parseStorePath(std::string(path));193 auto info = nixStore->queryPathInfo(sp);194 out.nar_hash =195 rust::String(info->narHash.to_string(nix::HashFormat::Nix32, true));196 out.nar_size = info->narSize;197 for (auto &r : info->references)198 out.references.push_back(rust::String(nixStore->printStorePath(r)));199 for (auto &s : info->sigs)200 out.sigs.push_back(rust::String(s.to_string()));201 } catch (const std::exception &e) {202 out.error = rust::String(e.what());203 }204 return out;205}206207CxxBuildResult compute_closure(Store *store, rust::Str path) {208 CxxBuildResult res{rust::String(), {}};209 try {210 auto nixStore = store->ptr;211 auto root = nixStore->parseStorePath(std::string(path));212 nix::StorePathSet closure;213 nixStore->computeFSClosure(root, closure);214 for (auto &p : closure)215 res.outputs.push_back(rust::String(nixStore->printStorePath(p)));216 } catch (const std::exception &e) {217 res.error = rust::String(e.what());218 }219 return res;220}221222namespace {223struct RustFnSink : nix::Sink {224 size_t data;225 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink;226 RustFnSink(size_t data,227 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink)228 : data(data), sink(sink) {}229 void operator()(std::string_view chunk) override {230 bool ok = sink(data, rust::Slice<const uint8_t>(231 reinterpret_cast<const uint8_t *>(chunk.data()),232 chunk.size()));233 if (!ok)234 throw nix::Error("nar sink aborted");235 }236};237} // namespace238239rust::String nar_from_path(Store *store, rust::Str path, size_t sink_data,240 rust::Fn<bool(size_t, rust::Slice<const uint8_t>)> sink) {241 try {242 auto nixStore = store->ptr;243 auto sp = nixStore->parseStorePath(std::string(path));244 RustFnSink s(sink_data, sink);245 nixStore->narFromPath(sp, s);246 return rust::String();247 } catch (const std::exception &e) {248 return rust::String(e.what());249 }250}251252AddFileToStoreResult add_file_to_store(Store *store, rust::Str name,253 rust::Str path) {254 AddFileToStoreResult out{rust::String(), rust::String(), rust::String()};255 try {256 auto nixStore = store->ptr;257 auto src = nix::PosixSourceAccessor::createAtRoot(258 std::filesystem::path(std::string(path)));259 auto info = nixStore->addToStoreSlow(std::string(name), src,260 nix::ContentAddressMethod::Raw::Flat,261 nix::HashAlgorithm::SHA256);262 out.store_path = rust::String(nixStore->printStorePath(info.path));263 if (info.ca.has_value()) {264 out.hash =265 rust::String(info.ca->hash.to_string(nix::HashFormat::SRI, true));266 }267 } catch (const std::exception &e) {268 out.error = rust::String(e.what());269 }270 return out;271}